mirror of
https://github.com/wassname/talk.git
synced 2026-07-30 12:40:41 +08:00
Fix merge conflicts
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Frequently Asked Questions
|
||||
permalink: /docs/faq/
|
||||
---
|
||||
|
||||
### How are new stories/assets added to Talk? Is there an API?
|
||||
|
||||
There are three ways that new assets can make their way into Talk: _just in time_, _active_ and manual.
|
||||
|
||||
#### Just in Time asset creation
|
||||
|
||||
Talk ships with a _just in time_ mechanism that works out of the box without integration with any CMS or manual work needed.
|
||||
|
||||
The _just in time_ flow looks like this:
|
||||
|
||||
* Request comes in for a stream on an asset that doesn't yet exist.
|
||||
* Talk screens the domain against the domain whitelist, fails if doesn't pass.
|
||||
* Then, concurrently
|
||||
* Talk creates a new asset record and returns the stream data (which will be empty)
|
||||
* Schedules a job to scrape the new page and fill in asset information.
|
||||
|
||||
The scraping mechanism utilizes [metascraper](https://www.npmjs.com/package/metascraper) and is queued using the [Que](https://www.npmjs.com/package/kue). If your Talk deployments is configured to run separate job worker cluster, scraping will be performed by them.
|
||||
|
||||
#### Active (or push based) asset creation
|
||||
|
||||
If tighter CMS integration is required to push custom data into assets and/or keep data in sync as changes are made in a CMS an _active_ push based workflow must be implemented.
|
||||
|
||||
This is an ideal candidate for a plugin. If you are interested in working on it, please [contact us](https://coralproject.net/contact)!
|
||||
|
||||
#### Manual asset creation
|
||||
|
||||
Sometimes you want to load a lot of assets into the database. The most common use case for this is populating the database during an initial installation. We recommend writing a script that transforms the data from it's source and inserts it into the _assets_ collection.
|
||||
|
||||
For current schema information, please see the [asset model](https://github.com/coralproject/talk/blob/master/models/asset.js).
|
||||
|
||||
### Where are your http API docs?
|
||||
|
||||
Coral relies on GraphQL for the vast majority of it's client <-> server communication. All core queries, mutations and subscriptions are defined along with types and comments in our central [TypeDef](https://github.com/coralproject/talk/blob/master/graph/typeDefs.graphql). For plugin graph api typedefs, see each plugin's `/server/` directory.
|
||||
|
||||
In addition, Talk Server ships with [GraphiQL](https://github.com/graphql/graphiql). GraphiQL provides a full data layer IDE including interactive documentation. The autocompletes and documentation are populated from introspection meaning that Core _and plugin_ apis will be fully explorable.
|
||||
|
||||
To access GraphiQL:
|
||||
|
||||
* [Install Talk](install-source).
|
||||
* Open http://localhost:3000/api/v1/graph/iql in your browser. (Note, your server an port may differ.)
|
||||
|
||||
### Where is documentation for a specific component?
|
||||
|
||||
We strive for clear inline documentation across our codebase, but have gaps. Contributions to documentation would be greatly appreciated and is a great way to start contributing to the project!
|
||||
|
||||
If you are considering changing a core component (aka, one that is not in a plugin), you are entering the realm of a core developer. We strongly ask that you reach out the coral team before forking and changing core code. We are glad to help talk through your product need and come up with a strategy for implementing as a plugin, or working with you to extend the plugin API for your use case.
|
||||
|
||||
### How do I contribute to these docs?
|
||||
|
||||
Contributions to the docs are much appreciated and a great way to get involved in the project.
|
||||
|
||||
Fork the Talk repo, clone it locally (no need to go through the install from source process), then:
|
||||
|
||||
```
|
||||
cd docs
|
||||
docker build --no-cache -t mydocs .
|
||||
docker run -v "$PWD:/src" -p 4000:4000 mydocs serve -H 0.0.0.0
|
||||
```
|
||||
|
||||
You can edit the files in docs with any editor and view the live updates in a browser by hitting `http://localhost:4000`.
|
||||
|
||||
Once you've made the changes, file a PR back to the `coralproject/talk` repo.
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: Installation Overview
|
||||
permalink: /docs/install/
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
Talk requires MongoDB and Redis.
|
||||
|
||||
- MongoDB ^3.2 - [Docs](https://docs.mongodb.com/manual/installation/)
|
||||
- Redis ^3.2.5 - [Docs](https://redis.io/topics/quickstart)
|
||||
|
||||
## Installation method
|
||||
|
||||
While Talk can be installed in many ways, we support two install paths:
|
||||
|
||||
* [Install from Source](source) (development)
|
||||
* [Install via Docker](docker) (deployment)
|
||||
|
||||
If you have success installing Talk in another way, please consider
|
||||
[contributing to this documentation](faq#how-do-i-contribute-to-these-docs)!
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: Installation From Docker
|
||||
permalink: /docs/install/docker/
|
||||
---
|
||||
|
||||
We currently support packaging the Talk application via Docker, which automates
|
||||
the dependency install and asset build process. This is the recommended way to
|
||||
deploy the application when used in production.
|
||||
|
||||
Available as [coralproject/talk](https://hub.docker.com/r/coralproject/talk/) on Docker Hub.
|
||||
|
||||
Images are tagged using the following notation:
|
||||
|
||||
- `x` (where `x` is the major version number): any minor or patch updates will be included in this. If you're ok getting
|
||||
new features occasionally and all the bug fixes, this is the tag for you.
|
||||
- `x.y` (where `y` is the minor version number): any patch updates will be
|
||||
included with this tag. If you like getting fixes and having features change
|
||||
only when you want, this is the tag for you. **(recommended)**
|
||||
- `x.y.z` (where `z` is the patch version): this tag never gets updated, and
|
||||
essentially freezes your version, this should only be used when you are either
|
||||
extending Talk or are sure of a specific version you want to freeze.
|
||||
|
||||
We provide tags with `*-onbuild` that can be used for easy plugin integration and
|
||||
acts as a customization endpoint. Instructions are provided in the `PLUGINS.md`
|
||||
document as to how to use it.
|
||||
|
||||
### Requirements
|
||||
|
||||
There are some runtime requirements for running Talk for Docker:
|
||||
|
||||
- [Docker](https://www.docker.com/) v1.13.0 or later
|
||||
- [Docker Compose](https://docs.docker.com/compose/) v1.10.0 or later
|
||||
|
||||
_Please be sure to check the versions of these requirements. Incorrect versions
|
||||
of these may lead to unexpected errors!_
|
||||
|
||||
### Installing
|
||||
|
||||
An example docker-compose.yml:
|
||||
|
||||
```yaml
|
||||
version: '2'
|
||||
services:
|
||||
talk:
|
||||
image: coralproject/talk:1.5
|
||||
restart: always
|
||||
ports:
|
||||
- "5000:5000"
|
||||
depends_on:
|
||||
- mongo
|
||||
- redis
|
||||
environment:
|
||||
- TALK_MONGO_URL=mongodb://mongo/talk
|
||||
- TALK_REDIS_URL=redis://redis
|
||||
mongo:
|
||||
image: mongo:3.2
|
||||
restart: always
|
||||
volumes:
|
||||
- mongo:/data/db
|
||||
redis:
|
||||
image: redis:3.2
|
||||
restart: always
|
||||
volumes:
|
||||
- redis:/data
|
||||
volumes:
|
||||
mongo:
|
||||
external: false
|
||||
redis:
|
||||
external: false
|
||||
```
|
||||
|
||||
At this stage, you should refer to the `README.md` for configuration variables
|
||||
that are specific to your installation. Some pre-defined fields have been filled
|
||||
in the above example which are consistent with Docker Compose naming conventions
|
||||
for [Docker Links](https://docs.docker.com/compose/networking/#links).
|
||||
|
||||
### Scaling
|
||||
|
||||
If you are interested in splitting apart services, you can simply adjust the
|
||||
command being executed in the container to optimize for your use case. An
|
||||
example would be if you wanted to run the API server and the job processor
|
||||
on different machines. You can achieve this easily with docker compose:
|
||||
|
||||
```yaml
|
||||
version: '2'
|
||||
services:
|
||||
talk-api:
|
||||
image: coralproject/talk:1.5
|
||||
command: cli serve
|
||||
restart: always
|
||||
ports:
|
||||
- "5000:5000"
|
||||
depends_on:
|
||||
- mongo
|
||||
- redis
|
||||
environment:
|
||||
- TALK_MONGO_URL=mongodb://mongo/talk
|
||||
- TALK_REDIS_URL=redis://redis
|
||||
talk-jobs:
|
||||
image: coralproject/talk:1.5
|
||||
command: cli jobs process
|
||||
restart: always
|
||||
ports:
|
||||
- "5001:5000"
|
||||
depends_on:
|
||||
- mongo
|
||||
- redis
|
||||
environment:
|
||||
- TALK_MONGO_URL=mongodb://mongo/talk
|
||||
- TALK_REDIS_URL=redis://redis
|
||||
mongo:
|
||||
image: mongo:3.2
|
||||
restart: always
|
||||
volumes:
|
||||
- mongo:/data/db
|
||||
redis:
|
||||
image: redis:3.2
|
||||
restart: always
|
||||
volumes:
|
||||
- redis:/data
|
||||
volumes:
|
||||
mongo:
|
||||
external: false
|
||||
redis:
|
||||
external: false
|
||||
```
|
||||
|
||||
Note that the only difference is in the `command` key. From this, you are able
|
||||
to discretely control which modules are running in order to have the maximum
|
||||
flexibility when managing your application.
|
||||
|
||||
### Running
|
||||
|
||||
If you're using docker compose:
|
||||
|
||||
```bash
|
||||
# Start the services using compose
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
If you're using plain docker:
|
||||
|
||||
```bash
|
||||
docker run -d -P coralproject/talk:latest
|
||||
```
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: Installation From Source
|
||||
permalink: /docs/install/source/
|
||||
---
|
||||
|
||||
This provides information on how to setup the application from source. Note that
|
||||
this is not recommended for production deploys, but will work for development
|
||||
and testing purposes.
|
||||
|
||||
## Requirements
|
||||
|
||||
There are some runtime requirements for running Talk from source:
|
||||
|
||||
- [Node](https://nodejs.org/) ~7.8
|
||||
- [Yarn](https://yarnpkg.com/) ^0.22.0
|
||||
|
||||
_Please be sure to check the versions of these requirements. Incorrect versions
|
||||
of these may lead to unexpected errors!_
|
||||
|
||||
## Installing
|
||||
|
||||
### Download
|
||||
|
||||
It is highly recommended that you download a released version as the code
|
||||
available in `master` may not be stable. You can download the latest release
|
||||
from the [releases page](https://github.com/coralproject/talk/releases).
|
||||
|
||||
You can also clone the git repository via:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/coralproject/talk.git
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
We now have to install the dependencies and build the static assets.
|
||||
|
||||
```bash
|
||||
# Install package dependancies
|
||||
yarn
|
||||
|
||||
# Build static files
|
||||
yarn build
|
||||
```
|
||||
|
||||
After you create/modify the `plugins.json` (refer to `PLUGINS.md` for plugin
|
||||
docs) file, you can re-run the following to install their dependencies:
|
||||
|
||||
```bash
|
||||
# Reconcile plugins
|
||||
./bin/cli plugins reconcile
|
||||
|
||||
# Build static files
|
||||
yarn build
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
Refer to the `README.md` file for required configuration variables to add to the
|
||||
environment.
|
||||
|
||||
You can start the server after configuring the server using the command:
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
This will setup the server to serve everything on a single node.js process and
|
||||
is designed to be used in production.
|
||||
|
||||
You can see other scripts we've made available by consulting the `package.json`
|
||||
file under the `scripts` key including:
|
||||
|
||||
- `yarn test` run unit tests
|
||||
- `yarn e2e` run end to end tests
|
||||
- `yarn build-watch` watch for changes to client files and build static assets
|
||||
- `yarn dev-start` watch for changes to server files and reload the server while
|
||||
also sourcing a `.env` file in your local directory for configuration
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: First Setup
|
||||
permalink: /docs/install/setup/
|
||||
---
|
||||
|
||||
Once you've installed Talk (either via Docker or source), you still need to
|
||||
setup the application. If you are unfamiliar with any terminology used in the
|
||||
setup process, refer to the `TERMINOLOGY.md` document.
|
||||
|
||||
## Via Web
|
||||
|
||||
If you want to perform your setup via the web, you can navigate to your
|
||||
installation of Talk at the path `/admin/install`. There you will be asked a
|
||||
series of questions for your installation.
|
||||
|
||||
## Via CLI
|
||||
|
||||
If you want to perform your setup through the terminal, you can simply run:
|
||||
|
||||
```bash
|
||||
cli setup
|
||||
```
|
||||
|
||||
And follow the instructions to perform initial setup and create your first user
|
||||
account.
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
After setup is complete, you can then refer to the `/admin/configure` path to
|
||||
get the embed code that you can copy/paste onto your blog or website in order to
|
||||
start using Talk.
|
||||
|
||||
_In order for the embed to work correctly, you will need to whitelist the domain
|
||||
that is allowed to embed your site on the `/admin/configure` page, failure to do
|
||||
so will result in the comment stream not loading._
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: Microservice Deployments
|
||||
permalink: /docs/install/microservices/
|
||||
---
|
||||
|
||||
In Talk, we seek to deliver the simplicity of a monolith with the advantages of
|
||||
a microservice based infrastructure for those who want them.
|
||||
|
||||
To accomplish this, Talk has the ability to run with subsets of its overall
|
||||
functionality and contains architecture that allows them to operate logically as
|
||||
microservices when running in a single environment.
|
||||
|
||||
## Talk Server Functionalities
|
||||
|
||||
The Talk server serves several logically/architecturally distinct functions:
|
||||
|
||||
* A web server that
|
||||
* serves "public" assets (aka, the comment embed)
|
||||
* the GraphQL endpoints
|
||||
* A web socket server that handles subscriptions.
|
||||
* A jobs processor that handles queued operations.
|
||||
|
||||
In the documentation so far, we've discussed how to deploy all of these
|
||||
functionalities bundled into a single monolith application. This is convenient
|
||||
as there is minimal configuration and horizontal scaling is as easy as upping
|
||||
the number of servers behind a single load balancer.
|
||||
|
||||
## Separating Talk into Microservices
|
||||
|
||||
Talk can be run in two or more separate clusters of servers by
|
||||
enabling/disabling different bits of functionality: webserver, socket server and
|
||||
jobs server.
|
||||
|
||||
Each microservice would deploy with the same codebase and configuration.
|
||||
|
||||
Note that the `cli serve` command, which is responsible for starting the server,
|
||||
contains flags that control whether `jobs` and `websockets` are enabled.
|
||||
|
||||
```
|
||||
talk :) ]$ bin/cli serve --help
|
||||
|
||||
Options:
|
||||
|
||||
...
|
||||
-j, --jobs enable job processing on this thread
|
||||
-w, --websockets enable the websocket (subscriptions) handler on this thread
|
||||
...
|
||||
```
|
||||
|
||||
Each Talk Microservice cluster can be deployed in an identical manner described
|
||||
in the other docs in this section with the omission of the `-j` and/or `-w`
|
||||
flags.
|
||||
|
||||
With routing logic in front of the webserver cluster, separation between public
|
||||
and protected assets can be achieved.
|
||||
|
||||
## When should I consider separating?
|
||||
|
||||
Consider a microservice deployment if:
|
||||
|
||||
* you are running plugins that require intensive job processing
|
||||
* you do not want to simplicity of single cluster horizontal scaling and want to
|
||||
tune the economy and performance of your install.
|
||||
* You run into scaling issues serving websockets
|
||||
|
||||
At scale, combining separate concerns in a single process makes it very
|
||||
difficult to understand what is taking up resources. With microservices, each
|
||||
server could be configured to sit behind it's own load balance and scale
|
||||
independently. Each variety of process can always have just enough resources.
|
||||
|
||||
An install that heavily utilizes the jobs queue could see delays in http service
|
||||
because of heavy jobs processes and/or delays in the execution of jobs processes
|
||||
due to increased server load as a result of Node's single thread infrustructure.
|
||||
|
||||
## Deployment Methodologies
|
||||
|
||||
Note that there is no flag to separate the http routes on the webserver.
|
||||
Separating the http server functionalities can be accomplished by the routing of
|
||||
various routes to the correct http server via an external upstream proxy like
|
||||
Google Cloud's Load Balancer, AWS's ELB, or a websever like NGINX/Apache. This
|
||||
can ensure that sensitive areas, such as the `/admin/` route are not available
|
||||
outside the firewall.
|
||||
|
||||
Talk's job processors are synchronized by Redis, so as long as all Talk
|
||||
instances access the same Redis cluster no additional configuration is needed
|
||||
when launching an independent jobs cluster.
|
||||
|
||||
If there are any features of Talk that you believe should be disable-able via
|
||||
server flags, please let us know and consider contributing it to the project!
|
||||
|
||||
## Deployment Flows/Scripts
|
||||
|
||||
We do not currently support any microservice based deployment flows. If you
|
||||
develop one yourself that is completely based on open source tooling, please
|
||||
consider contributing it to the project!
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Installation Troubleshooting
|
||||
permalink: /docs/install/troubleshooting/
|
||||
---
|
||||
|
||||
This page tracks common issues that arise when installing Talk and provides resolutions.
|
||||
|
||||
## The Talk server seems to be working but the stream isn't showing up on my page.
|
||||
|
||||
Talk employs a _domain whitelist_ that controls which sites can contain comment threads. This prevents malicious folks from using your server to embed streams on unwanted pages.
|
||||
|
||||
If your comment thread isn't showing:
|
||||
|
||||
1. Log into your admin panel
|
||||
1. Go to the Configure tab
|
||||
1. Select the Tech Settings submenu
|
||||
1. Ensure that your Domain is the Permitted Domains list
|
||||
|
||||
Note: if your site has multiple subdomains, listing the domain itself (ie: `mydomain.com`) will enable Talk on all subdomains. If you would like to restrict Talk to certain subdomains, you must list all of them here (ie: `thisone.mydomain.com thatone.mydomain.com`).
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: Configuration
|
||||
permalink: /docs/running/configuration/
|
||||
---
|
||||
|
||||
{% include toc %}
|
||||
|
||||
## Overview
|
||||
{:.no_toc}
|
||||
|
||||
Talk, like many web applications, requires manual configuration via environment
|
||||
variables to configure the server for your specific needs. This is following the
|
||||
standard [12 Factor App Manifesto](https://12factor.net/) which requires that
|
||||
said configuration lives as environment variables.
|
||||
|
||||
During development, we can utilize a `.env` file, which takes the form of
|
||||
`NAME=VALUE`. For example:
|
||||
|
||||
```bash
|
||||
TALK_MONGO_URL=mongodb://some-awesome-mongo-instance
|
||||
TALK_REDIS_URL=redis://some-awesome-redis-instance
|
||||
TALK_ROOT_URL=https://my-awesome-talk.com
|
||||
# ... and so on
|
||||
```
|
||||
|
||||
When you place a file titled `.env` at the root of the project, and start the
|
||||
application with `yarn dev-start`, it will read in the contents of the `.env`
|
||||
file as if they were environment variables. This is done via the
|
||||
[dotenv](https://github.com/motdotla/dotenv) package. In production, using this
|
||||
method is discouraged, as it promotes bad practices such as storing config in a
|
||||
file.
|
||||
|
||||
## Variables
|
||||
|
||||
The Talk application looks for the following configuration values either as
|
||||
environment variables. Refer to the
|
||||
[config.js](https://github.com/coralproject/talk/blob/master/config.js) file to
|
||||
see how the configuration is parsed.
|
||||
|
||||
### Database
|
||||
|
||||
- `TALK_MONGO_URL` (*required*) - the database connection string for the MongoDB database.
|
||||
- `TALK_REDIS_URL` (*required*) - the database connection string for the Redis database.
|
||||
|
||||
### Server
|
||||
|
||||
- `TALK_ROOT_URL` (*required*) - root url of the installed application externally
|
||||
available in the format: `<scheme>://<host>` without the path.
|
||||
- `TALK_KEEP_ALIVE` (_optional_) - The keepalive timeout that should be used to
|
||||
send keep alive messages through the websocket to keep the socket alive. (Default `30s`)
|
||||
- `TALK_INSTALL_LOCK` (_optional for dynamic setup_) - When `TRUE`, disables the dynamic setup endpoint. (Default `FALSE`)
|
||||
|
||||
### JWT
|
||||
|
||||
The following are configuration shared with every type of secret used.
|
||||
|
||||
- `TALK_JWT_ALG` (_optional_) - the algorithm used to sign/verify JWT's used for
|
||||
session management. Read up about alternative algorithms on the
|
||||
[jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken#algorithms-supported) package. (Default `HS256`)
|
||||
- `TALK_JWT_EXPIRY` (_optional_) - the expiry duration (`exp`) for the tokens
|
||||
issued for logged in sessions. (Default `1 day`)
|
||||
- `TALK_JWT_ISSUER` (_optional_) - the issuer (`iss`) claim for login JWT
|
||||
tokens. (Default `process.env.TALK_ROOT_URL`)
|
||||
- `TALK_JWT_AUDIENCE` (_optional_) - the audience (`aud`) claim for login JWT
|
||||
tokens. (Default `talk`)
|
||||
- `TALK_JWT_COOKIE_NAME` (_optional_) - the name of the cookie to extract the
|
||||
JWT from (Default `authorization`)
|
||||
- `TALK_JWT_CLEAR_COOKIE_LOGOUT` (_optional_) - when `FALSE`, Talk will not
|
||||
clear the cookie with name `TALK_JWT_COOKIE_NAME` when logging out (Default
|
||||
`TRUE`)
|
||||
|
||||
**You must also specify secrets as either the `TALK_JWT_SECRET` or the `TALK_JWT_SECRETS`
|
||||
variable. Refer to the [Secrets Documentation]({{ "/docs/running/secrets/" | absolute_url }})
|
||||
on the contents of those variables.**
|
||||
|
||||
### Email
|
||||
|
||||
- `TALK_SMTP_EMAIL` (*required for email*) - the address to send emails from
|
||||
using the SMTP provider.
|
||||
- `TALK_SMTP_USERNAME` (*required for email*) - username of the SMTP provider
|
||||
you are using.
|
||||
- `TALK_SMTP_PASSWORD` (*required for email*) - password for the SMTP provider
|
||||
you are using.
|
||||
- `TALK_SMTP_HOST` (*required for email*) - SMTP host url with format
|
||||
`smtp.domain.com`, note the lack of protocol on the domain.
|
||||
- `TALK_SMTP_PORT` (*required for email*) - SMTP port.
|
||||
|
||||
|
||||
### Recaptcha
|
||||
|
||||
- `TALK_RECAPTCHA_SECRET` (*required for reCAPTCHA support*) - server secret used for enabling reCAPTCHA powered logins. If not provided it will instead default to providing only a time based lockout.
|
||||
- `TALK_RECAPTCHA_PUBLIC` (*required for reCAPTCHA support*) - client secret used for enabling reCAPTCHA powered logins. If not provided it will instead default to providing only a time based lockout.
|
||||
|
||||
### Plugins
|
||||
|
||||
- `TALK_PLUGINS_JSON` (_optional_) - used to specify the plugin config via the
|
||||
environment.
|
||||
- `TALK_DISABLE_AUTOFLAG_SUSPECT_WORDS` (_optional_) When `TRUE`, disables flagging of comments that match the suspect word filter. (Default `FALSE`)
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: Secrets
|
||||
permalink: /docs/running/secrets/
|
||||
---
|
||||
|
||||
{% include toc %}
|
||||
|
||||
## Secret Types
|
||||
|
||||
We support two types of secrets.
|
||||
|
||||
- Shared secrets
|
||||
- Asymmetric Secrets
|
||||
|
||||
### Shared Secret
|
||||
|
||||
You would use a shared secret when you have no need to share the tokens with
|
||||
other applications in your organization.
|
||||
|
||||
Supported signing algorithms:
|
||||
|
||||
- HS256
|
||||
- HS384
|
||||
- HS512
|
||||
|
||||
These must be provided in the form:
|
||||
|
||||
```json
|
||||
{
|
||||
"secret": "<my secret key>"
|
||||
}
|
||||
```
|
||||
|
||||
### Asymmetric Secret
|
||||
|
||||
You would use a asymmetric secret when you want to share the token in your
|
||||
organization, and would like to pass an existing auth token to Talk in order to
|
||||
authenticate your users. (Documentation on how to do this is pending!).
|
||||
|
||||
Supported signing algorithms:
|
||||
|
||||
- RS256
|
||||
- RS384
|
||||
- RS512
|
||||
- ES256
|
||||
- ES384
|
||||
- ES512
|
||||
|
||||
These must be provided in the form:
|
||||
|
||||
```json
|
||||
{
|
||||
"public": "<the PEM encoded public key>",
|
||||
"private": "<the PEM encoded private key>"
|
||||
}
|
||||
```
|
||||
|
||||
Note that when using the asymmetric keys as discussed above, the certificates
|
||||
must have their newlines replaced with `\\n`, this is to ensure that the
|
||||
newlines are preserved after JSON decoding. Not doing so will result in parsing
|
||||
errors.
|
||||
|
||||
## Authentication Types
|
||||
|
||||
Talk also supports two methods of providing authenticationd details.
|
||||
|
||||
- Single key: this is used when your secrets do not need to be rotated.
|
||||
- Multiple keys: this is used when you expect to rotate your secrets.
|
||||
|
||||
### Single Key
|
||||
|
||||
When using a single key, you can utilize the following configuration style:
|
||||
|
||||
- `TALK_JWT_SECRET` (*required*) - The shared secret or certificate (depending
|
||||
on your choice of `TALK_JWT_ALG`) used for jwt tokens.
|
||||
|
||||
An example of this:
|
||||
|
||||
```bash
|
||||
# for a shared secret
|
||||
TALK_JWT_SECRET={"secret": "<my secret string>"}
|
||||
|
||||
# for a asymmetric secret
|
||||
TALK_JWT_SECRET={"private": "<my private key>", "public": "<my public key>"}
|
||||
```
|
||||
|
||||
### Multiple Key
|
||||
|
||||
When using a multiple keys, you can utilize the following configuration style:
|
||||
|
||||
- `TALK_JWT_SECRETS` (_optional_) - used when specifying multiple secrets used
|
||||
for key rotations. This is a JSON encoded array, where each element matches
|
||||
the JWT Secret pattern.
|
||||
|
||||
All secrets also get a `kid` field which uniquely identifies a given key and
|
||||
will sign all tokens with that `kid` for later identification.
|
||||
|
||||
An example of this:
|
||||
|
||||
```bash
|
||||
# for a shared secret
|
||||
TALK_JWT_SECRETS=[{"kid" "1", "secret": "<my secret string>"}, {"kid" "2", "secret": "<my other secret string>"}]
|
||||
|
||||
# for a asymmetric secret
|
||||
TALK_JWT_SECRETS=[{"kid": "1", "private": "<my private key>", "public": "<my public key>"}, {"kid": "2", "private": "<my other private key>", "public": "<my other public key>"}]
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Database Migrations
|
||||
permalink: /docs/running/migrations/
|
||||
---
|
||||
|
||||
On major version changes, database migrations are usually required. The
|
||||
application will refuse to start until all pending migrations are ran. This also
|
||||
applies to empty databases.
|
||||
|
||||
## Running Migrations
|
||||
|
||||
We have a migration tool that can be run using `bin/cli migration run`. This
|
||||
will detect new migrations available and prompt you to backup your database
|
||||
before proceeding with the migration. Migrations are required with major version
|
||||
releases.
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Architecture Overview
|
||||
permalink: /docs/architecture/
|
||||
---
|
||||
|
||||
## Talk's Architecture
|
||||
|
||||
Talk consists of four distinct layers of code:
|
||||
|
||||
* Plugins
|
||||
* Plugin API
|
||||
* Core
|
||||
* cli
|
||||
|
||||
### Plugins
|
||||
|
||||
Talk plugins deliver the features and functionality that can be changed or removed. Much of the default functionality is delivered by core plugins allowing developers to have control over any non-essential functionality.
|
||||
|
||||
### Plugin API
|
||||
|
||||
Talk plugins interact exclusively with the Plugin API. Maintaining this layer of separation between plugins and core allows us to consciously design the api that we want it publish to plugin authors. We can then expose just the elements of core that make sense and maintain that contract as core changes.
|
||||
|
||||
### Core
|
||||
|
||||
Talk core consists of architecture and functionality that deliver stability, security, scalability, extendability, etc... In addition, the Core contains features and functionality that is essential to the operation of Talk as a product.
|
||||
|
||||
Our goal is to continually extend our plugin infrastructure making the Core as pluggable as possible. Ultimately, a day may come where the Core of Talk is simply a framework for delivering a certain flavor of web applications.
|
||||
|
||||
### cli
|
||||
|
||||
Talk ships with [a cli tool]({{ "/docs/architecture/cli" | absolute_url }}) that exposes functionality to the command line. We seek to provide cli functionality for all features that could need to be accomplished programmatically or prior to the server's startup.
|
||||
|
||||
## Thinking about Plugins, the Plugin API and Core?
|
||||
|
||||
The following is a template for a thought process that may help clarify your ideas against the backdrop of Talk's architecture.
|
||||
|
||||
Think of a feature or capability. It could be something that's already in Talk or not. It could be something you want to build, or something you'd think would be a terrible idea. The important part here is to have something to interrogate.
|
||||
|
||||
```
|
||||
wait(60000);
|
||||
```
|
||||
|
||||
Now, ask these questions:
|
||||
|
||||
### Is it a Plugin?
|
||||
|
||||
Most work for Talk happens in the Plugin space. If the answers to any of these questions is Yes, then you're thinking of a Plugin.
|
||||
|
||||
* Does Talk's existing Plugin APIs support the thing you want to build?
|
||||
* Is this something that only some users will want/need?
|
||||
* Is this something that we want devs to iterate on widely?
|
||||
|
||||
You should [build it as a plugin]({{ "/docs/plugins/quickstart" | absolute_url }}). Feel free to explore here on your own or reach out to us. We love to advise on plugins, so please feel free to [let us know]({{ "/docs/development/contributing" | absolute_url }}#writing-code) and we will start a conversation. We will help you conceptualize, architect and promote your plugin if it is in line with our values.
|
||||
|
||||
### Does it need updates to the Plugin API?
|
||||
|
||||
If you answered yes above:
|
||||
|
||||
* Do I need to extend the Plugin API to support my plugin?
|
||||
|
||||
Often times all the functionality a plugin needs is in the Core, but the Plugin API doesn't expose it. In these cases, we seek to iteratively extend the Talk Plugin API. All Plugin API contributions from the community must begin by [let us know]({{ "/docs/development/contributing" | absolute_url }}#writing-code).
|
||||
|
||||
Note: we are stabilizing the process by which new Plugin API bindings are created, agreed upon and ultimately made part of our Plugins Contract. If you are interested in this process, please reach out to us.
|
||||
|
||||
### Does it require updates to the Plugin API _and_ Core?
|
||||
|
||||
What, if any, changes need to be made to Core so that the API can be extended?
|
||||
|
||||
Quite often the only things missing from Core are things like _events_, _slots_, _CSS classes_, etc... Adding these is a great way to become a Core Contributor and break new ground as a Plugin Developer.
|
||||
|
||||
We seek to keep Core as lean as possible.
|
||||
|
||||
### Is my idea really just Core?
|
||||
|
||||
Amazing! We are always looking to extend the capabilities of Talk. We look forward to discussing what you've got to bring!
|
||||
|
||||
Please see our [contributing guide]({{ "/docs/development/contributing" | absolute_url }}) for more information.
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Tags
|
||||
permalink: /docs/architecture/tags/
|
||||
---
|
||||
|
||||
Tags are essentially strings that can be added to models. Currently, tags can be added to [Users, Comments and Assets](https://github.com/coralproject/talk/blob/ced449a1489d47c25d604020fa2e0b3b7a741353/graph/typeDefs.graphql#L144). If you would like to add tags to other models, you can extend this schema using [GraphQL hooks]({{ "/docs/plugins/server" | absolute_url }}#graphql-hooks).
|
||||
|
||||
## Tag Definitions
|
||||
|
||||
When handling tags, the Talk Server references a set of definitions that describe how tags are handled. These definitions are keyed off the tag `name`, the simple string that is stored on items.
|
||||
|
||||
The schema for Tag definitions [can be found here](https://github.com/coralproject/talk/blob/3545bf01cd91044fdb738d337a0ac94d9f71fbc3/models/schema/tag.js).
|
||||
|
||||
Note that along with the `name`, tag definitions contains:
|
||||
|
||||
* `permissions` information about who can see and set the tag,
|
||||
* `models` which `ITEM_TYPES` this tag can be applied to, and
|
||||
|
||||
Whenever a tag is 'handled' by the server, it references this definition to determine that tag's behavior.
|
||||
|
||||
See [Plugin API Documentation]({{ "/docs/plugins/server" | absolute_url }}#field-tags) for more information.
|
||||
|
||||
### Creating a Tag Definition
|
||||
|
||||
Tag Definitions must be created in order for the system to determine what tags are permitted on the server side.
|
||||
|
||||
Tag Definitions do not contain any logic themselves but provide information that other parts of the system can use to specify which models a tag can be applied to (models) and perform authorization logic (permissions).
|
||||
|
||||
Take the tag created by `talk-plugin-offtopic` as an example.
|
||||
|
||||
```
|
||||
// talk-plugin-offtopic/index.js
|
||||
module.exports = {
|
||||
tags: [
|
||||
{
|
||||
name: 'OFF_TOPIC',
|
||||
permissions: {
|
||||
public: true,
|
||||
self: true,
|
||||
roles: []
|
||||
},
|
||||
models: ['COMMENTS'],
|
||||
created_at: new Date()
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
This plugin allows users to self-report that their comment is "off topic" at the time of creation, then display a badge on those comments.
|
||||
|
||||
To accomplish this, the plugin creates the tag `OFF_TOPIC` with:
|
||||
|
||||
* `permissions.public: true` - will be sent over the wire to the client side
|
||||
* `permissions.self: true` - can be added by the active user to themselves or assets they own
|
||||
* `permissions.roles: []` - cannot be added by anyone based on their roles
|
||||
* `models: ['COMMENTS']` - can only be added to COMMENTS (not to users/assets/etc...)
|
||||
|
||||
And [viola](https://youtu.be/Q0O9gFf-tiI?t=23s)! This tag is something that can only be created by the logged in user on their own comments and is sent over the wire to the client so it can display the badge.
|
||||
|
||||
## Tag Links
|
||||
|
||||
When tags are stored on objects in the database, they are represented by [TagLinks](https://github.com/coralproject/talk/blob/master/models/schema/tag_link.js).
|
||||
|
||||
A TagLinks says that `tag` was `assigned_by` a specific user at a specific time (`created_at`).
|
||||
|
||||
Note that the `tag` field in the TagLinkSchema is the full TagSchema itself. This allows for another level of flexibility. Server code may generate Tags on the fly, complete with programmatically generated permissions and item behaviors.
|
||||
|
||||
If a Tag definitions exists in the global/asset context then that definition will be used regardless of what is stored here. This allows high level controls on the behavior of tags, ensuring that plugins cannot produce unexpected definitions for already defined tags.
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: Metadata
|
||||
permalink: /docs/architecture/metadata/
|
||||
---
|
||||
|
||||
_Metadata_ allows you to add fields to models that are not represented in the core schema.
|
||||
|
||||
## Goals
|
||||
|
||||
The metadata api is designed to satisfy two product goals:
|
||||
|
||||
* Give developers flexibility in extending datatypes.
|
||||
* Protect core fields that are essential to Talk's operation.
|
||||
|
||||
## Design
|
||||
|
||||
Metadata is represented by an [subdocument in our Schemas](https://github.com/coralproject/talk/blob/c59c09e1f42c51eed3b0d57b7c2882fc7b5edc13/models/comment.js#L74). This takes advantage of Mongo's flexibility allowing for any data to be stored therein.
|
||||
|
||||
### Setting Metadata
|
||||
|
||||
Talk provides [a service layer](https://github.com/coralproject/talk/blob/c59c09e1f42c51eed3b0d57b7c2882fc7b5edc13/services/metadata.js) allowing developers to `set` and `unset` metadata on objects in a way similar to key-value stores.
|
||||
|
||||
Let's say that I want to add a custom field called `potency` to a comment.
|
||||
|
||||
```
|
||||
const MetadataService = require('services/metadata');
|
||||
const CommentModel = require('models/comment');
|
||||
|
||||
// Sets the property `potency` on the comment with `id=1`.
|
||||
MetadataService.set(CommentModel, '1', 'potency', 42);
|
||||
```
|
||||
|
||||
Note that the model passed here is the Model itself and not an individual comment object. This allows us to update the value on that document [in an atomic manner](https://github.com/coralproject/talk/blob/c59c09e1f42c51eed3b0d57b7c2882fc7b5edc13/services/metadata.js#L60) for efficiency and to prevent race conditions.
|
||||
|
||||
### Accessing Metadata
|
||||
|
||||
The metadata api does not contain a `get` method. The metadata object is retrieved via database queries along with the rest of the data.
|
||||
|
||||
## Metadata and the Graph
|
||||
|
||||
One of the first principles of GraphQL is that the shape of the graph does not need to be the same as the shape of the data in the database. In fact, it probably shouldn't be.
|
||||
|
||||
This enables us to treat metadata fields in any way that makes sense as we design our Graph. The fact that a value is stored in the metadata object is an implementation detail invisible to the front end.
|
||||
|
||||
Take for example, the `reason` field in the `FlagAction` type. This stores the user provided reason why they flagged a comment. As far as the front end knows, it's [just another field](https://github.com/coralproject/talk/blob/c59c09e1f42c51eed3b0d57b7c2882fc7b5edc13/graph/typeDefs.graphql#L453) alongside the core fields:
|
||||
|
||||
```
|
||||
# graph/typeDefs.graphql
|
||||
type FlagAction implements Action {
|
||||
|
||||
...
|
||||
|
||||
# The reason for which the Flag Action was created.
|
||||
reason: String
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
If, however, we [look at the resolver](https://github.com/coralproject/talk/blob/a47e2378e96f34f25447782f3e7ce59fa48ec791/graph/resolvers/dont_agree_action.js) for that field, we see that `reason` is [destructured](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) from the metadata object and returned.
|
||||
|
||||
```
|
||||
// graph/resolvers/dont_agree_action.js
|
||||
const DontAgreeAction = {
|
||||
|
||||
// Stored in the metadata, extract and return.
|
||||
reason({metadata: {reason}}) {
|
||||
return reason;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = DontAgreeAction;
|
||||
```
|
||||
|
||||
This is an extremely powerful pattern as it allows us absolute freedom in designing our graph and complete isolation of the added fields in the database.
|
||||
|
||||
## Some things to keep in mind
|
||||
|
||||
### Namespace your metadata fields
|
||||
|
||||
Since metadata can be added by the core and multiple plugins, collisions may occur. As you create your plugins, please be careful to pick unique names for metadata fields. We recommend namespacing all your fields in a subdocument named after your plugin.
|
||||
|
||||
```
|
||||
[model].metadata.[your_plugin_name].[the_field]
|
||||
```
|
||||
|
||||
### Querying by metadata fields
|
||||
|
||||
We currently do not have a clean way to index metadata fields. As a result queries that match against metadata fields will not scale. If you have a need to match, sort, etc... by a metadata field, [please let us know]({{ "/docs/development/contributing" | absolute_url }}#writing-code).
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: Command Line Interface (cli)
|
||||
permalink: /docs/architecture/cli/
|
||||
---
|
||||
|
||||
Talk ships with a cli tool that allows access to a wide variety of functionality.
|
||||
|
||||
It is designed to provide a convenient way for engineers to perform key tasks without the need to muck about in the UI. It also opens the door for scripts to perform operations programmatically.
|
||||
|
||||
Note: the cli tool requires [the Talk environment to be configured]({{ "/docs/running/configuration" | absolute_url }}) either via env vars or by using a `.cli` file via `bin/cli -c .env [command] ....`
|
||||
|
||||
## Using the cli
|
||||
|
||||
In a terminal, try:
|
||||
|
||||
```
|
||||
/path/to/talk/bin cli [options] [commands] [arguments]
|
||||
```
|
||||
|
||||
Commonly, you'll be in the `talk/` folder, in which case you can:
|
||||
|
||||
```
|
||||
bin/cli [options] [commands] [arguments]
|
||||
```
|
||||
|
||||
If you're a heavy cli user, consider adding the `bin` folder to your PATH so you can run `cli` from anywhere!
|
||||
|
||||
If you are using [our Docker environment]({{ "/docs/install/docker" | absolute_url }}), the bin folder will already be in the PATH.
|
||||
|
||||
## What can I do with the cli?
|
||||
|
||||
The Talk cli ships with 'unix style' help. To access the docs, simply run the cli with insufficient arguments.
|
||||
|
||||
Let's say I wanted to figure out how to change a user's password. I'd start be seeing what the cli has for me.
|
||||
|
||||
(Note: the following output may change, please reference at the `--help` output for your version as you use the cli.)
|
||||
|
||||
```
|
||||
talk :) ]$ bin/cli --help
|
||||
|
||||
Usage: cli [options] [command]
|
||||
|
||||
|
||||
Commands:
|
||||
|
||||
serve serve the application
|
||||
settings interact with the application settings
|
||||
assets interact with assets
|
||||
setup setup the application
|
||||
jobs work with the job queues
|
||||
token work with the access tokens
|
||||
users work with the application auth
|
||||
migration provides utilities for migrating the database
|
||||
plugins provides utilities for interacting with the plugin system
|
||||
help [cmd] display help for [cmd]
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help output usage information
|
||||
-V, --version output the version number
|
||||
-c, --config [path] Specify the configuration file to load
|
||||
--pid [path] Specify a path to output the current PID to
|
||||
```
|
||||
|
||||
Most commands contain sub-commands. Running with cli with such a command generates the docs for the sub-commands and options therein.
|
||||
|
||||
Change user password is likely to be in the `users` command group.
|
||||
|
||||
```
|
||||
talk :) ]$ bin/cli users
|
||||
|
||||
Usage: cli-users [options] [command]
|
||||
|
||||
|
||||
Commands:
|
||||
|
||||
create [options] create a new user
|
||||
delete <userID> delete a user
|
||||
passwd <userID> change a password for a user
|
||||
update [options] <userID> update a user
|
||||
list list all the users in the database
|
||||
merge <dstUserID> <srcUserID> merge srcUser into the dstUser
|
||||
addrole <userID> <role> adds a role to a given user
|
||||
removerole <userID> <role> removes a role from a given user
|
||||
ban <userID> ban a given user
|
||||
uban <userID> unban a given user
|
||||
disable <userID> disable a given user from logging in
|
||||
enable <userID> enable a given user from logging in
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help output usage information
|
||||
-V, --version output the version number
|
||||
-c, --config [path] Specify the configuration file to load
|
||||
--pid [path] Specify a path to output the current PID to
|
||||
```
|
||||
|
||||
I now see that I can change a password like so:
|
||||
|
||||
```
|
||||
bin/cli users passwd [userID]
|
||||
```
|
||||
|
||||
You can also read these help prompts by [exploring the source code](https://github.com/coralproject/talk/blob/master/bin/cli).
|
||||
|
||||
### Usage Notes
|
||||
|
||||
If you haven't used a cli enabled system before, think of it like this: generally, you'd make a rest call, rpc, etc... to perform an action. The cli's api is designed in the same way, just for the audience of command line wielding engineers and scripts.
|
||||
|
||||
The best way to understand what the cli does is to explore the help commands. Uses of cli are also scattered through this documentation in context of their topics.
|
||||
|
||||
For some real world uses of the cli, see the scripts in the [package.json file](https://github.com/coralproject/talk/blob/d688f70c19d8dee48371784009fd07322dae4eb5/package.json#L8).
|
||||
|
||||
## What's really going on when I run the cli?
|
||||
|
||||
The cli tool is a standalone application. Running it starts up the internals of a talk process, executes the given command, then shuts it down. No server functionality is enabled by cli commands unless specifically noted.
|
||||
|
||||
The cli tool _does not require a talk server to be running._ This means that you can execute commands, for example, during and installation process before starting the server. The also means that you can execute commands using varying configurations (via the `-c [.env file]` flag).
|
||||
|
||||
### Accessing existing Talk installs with the cli
|
||||
|
||||
You may use the cli tool to 'remotely' control existing talk installs.
|
||||
|
||||
This is accomplished by running the cli tool on any box using the mongo/redis/etc... credentials for the environment that you would like to act on. For example, if you want something to happen periodically on your production Talk install, you could set up a utility box with a cron job that triggers the cli with the same db/cache credentials. If you want to do something quick on a staging server, you could run the cli locally with staging credentials.
|
||||
|
||||
The cli tool will connect directly with the install's db and redis instance(s) so ensure that your box can reach those servers on the appropriate ports.
|
||||
|
||||
Also, _please ensure your cli and the server(s) in an environment are using the same version of Talk._
|
||||
|
||||
Please secure your environments and credentials or the cli tool becomes a convenient way for someone to own your system.
|
||||
|
||||
## Extending the cli
|
||||
|
||||
The Talk cli is based on the excellent [commander](https://github.com/tj/commander.js/) library.
|
||||
|
||||
At the time of writing, there are no plugin hooks for the cli tool. If you would like to change this, whether by writing code yourself or recommending a need, please [write and issue]({{ "/docs/development/contributing" | absolute_url }}#writing-code).
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
title: Client Architecture
|
||||
permalink: /docs/architecture/client
|
||||
---
|
||||
|
||||
## The Stack
|
||||
- [React](#react)
|
||||
- [Redux](#redux)
|
||||
- [ImmutableJS](#immutablejs)
|
||||
|
||||
|
||||
## The Architecture
|
||||
Our frontend lives within [talk/client](https://github.com/coralproject/talk/tree/153193959cb4dfa5d8feaabb49811325f836ee68/client) folder. Every folder contains a plugin. In [coral-framework](https://github.com/coralproject/talk/tree/153193959cb4dfa5d8feaabb49811325f836ee68/client/coral-framework) you will find the core architecture of Talk.
|
||||
Here is where our Redux Application, translations, components, and helpers live.
|
||||
|
||||
|
||||
## Presentational and Container Components
|
||||
We use a common simple pattern called
|
||||
__Presentational and Container Components__
|
||||
|
||||
It basically consist in having two types of components:
|
||||
- Presentational
|
||||
- Containers
|
||||
|
||||
### Presentational Components
|
||||
- __How our UI looks like__
|
||||
- Are stateless components
|
||||
- Render props
|
||||
- Allow containment of children via `this.props.children`
|
||||
- They have DOM Markup
|
||||
|
||||
### Container Components
|
||||
* __How things work__
|
||||
* They don’t have markup nor styles
|
||||
* They provide data and behaviour to Presentational or Container Components
|
||||
* They connect via `react-redux`’s `connect()` to the state.
|
||||
* They `mapStateToProps` the state to the Presentational Container.
|
||||
* They `mapDispatchToProps` to send actions to the Presentational Container.
|
||||
* Name Convention `<Name>Container.js`
|
||||
|
||||
How a container looks like:
|
||||
```js
|
||||
/*
|
||||
* mapStateToProps
|
||||
* We map the part of the state that we want to use
|
||||
*/
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
auth: state.auth.toJS()
|
||||
});
|
||||
|
||||
/*
|
||||
* mapDispatchToProps
|
||||
* We map the actions that we want to use
|
||||
*/
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
checkLogin: () => dispatch(checkLogin())
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* connect
|
||||
* We wrap our container in a connect() function
|
||||
*/
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(SignInContainer);
|
||||
````
|
||||
|
||||
How our SignInContainer works: [talk/SignInContainer.js · GitHub](https://github.com/coralproject/talk/blob/153193959cb4dfa5d8feaabb49811325f836ee68/client/coral-sign-in/containers/SignInContainer.js)
|
||||
|
||||
Within our plugins we create two folders `containers` and `components` so we can differentiate them:
|
||||
```
|
||||
coral-sign-in/
|
||||
├── containers/
|
||||
│ └── SignInContainer.js
|
||||
└── components/
|
||||
├── SignInContent.js
|
||||
└── SignUpContent.js
|
||||
```
|
||||
|
||||
More about this architecture:
|
||||
|
||||
[Container Components – Learn React with chantastic – Medium](https://medium.com/@learnreact/container-components-c0e67432e005#.w8mzgndcg)
|
||||
|
||||
|
||||
[Presentational and Container Components – Dan Abramov – Medium](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.ai4ih55v3)
|
||||
|
||||
|
||||
## GraphQL
|
||||
We use [Apollo](http://www.apollodata.com/) to handle graph requests and handle state.
|
||||
|
||||
## Redux
|
||||
We use [Redux](http://redux.js.org/) to handle the auth state.
|
||||
|
||||
|
||||
## ImmutableJS
|
||||
We use Immutable JS to maintain our state immutable.
|
||||
We found some really good tradeoffs while building Talk.
|
||||
|
||||
[How to use ImmutableJS and how we use it with Talk](https://facebook.github.io/immutable-js/docs/#/)
|
||||
|
||||
|
||||
## Test
|
||||
[How we do testing at Coral with Talk]({{ "/docs/development/tools" | absolute_url }})
|
||||
|
||||
|
||||
## Lint
|
||||
For linting in Talk we use `eslint:recommended`
|
||||
|
||||
You can find more info about the rules and best practices here:
|
||||
|
||||
http://eslint.org/docs/rules/#best-practices
|
||||
|
||||
## Lint the code
|
||||
```js
|
||||
yarn lint
|
||||
```
|
||||
|
||||
|
||||
## The Future of the Frontend
|
||||
- [Preact](https://github.com/developit/preact)
|
||||
- [Reselect](https://github.com/reactjs/reselect)
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: Plugins Overview
|
||||
permalink: /docs/plugins/
|
||||
---
|
||||
|
||||
## Recipes
|
||||
|
||||
Recipes are plugin templates provided by the Coral Core team. Developers can use these recipes to build their own plugins. You can find all the Talk recipes here: https://github.com/coralproject/talk-recipes/
|
||||
|
||||
## Plugin Registration
|
||||
|
||||
In order for a plugin to be active in a Talk install, it must be _registered_. The parsing order for the plugin registration is as follows:
|
||||
|
||||
- `TALK_PLUGINS_JSON` environment variable
|
||||
- `plugins.json` file
|
||||
- `plugins.default.json` file
|
||||
|
||||
If you need to "disable all plugins", you can simply provide `{}` as the
|
||||
contents of `process.env.TALK_PLUGINS_JSON` or the `plugins.json`.
|
||||
|
||||
The format for this is thus:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": [
|
||||
"people"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Where we have a `server` key with an array of plugins that match the folder
|
||||
name in the `plugins/` folder. For example, the above config would
|
||||
require a plugin from `plugins/people`, which must provide a `index.js` file
|
||||
that returns an object that matches the Plugin Specification.
|
||||
|
||||
If the package is external (available on NPM) you can specify the string for
|
||||
the version by using an object instead, for example:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": [
|
||||
{"people": "^1.2.0"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
External plugins can be resolved by running:
|
||||
|
||||
```bash
|
||||
./bin/cli plugins reconcile
|
||||
```
|
||||
|
||||
This achieves two things:
|
||||
|
||||
1. It will traverse into local plugin folders and install their dependencies.
|
||||
_Note that if the plugin is already installed and available in the node_modules folder, it will not be
|
||||
fetched again unless there is a version mismatch._ This will result in the
|
||||
project `package.json` and `yarn.lock` files to be modified, this is normal as
|
||||
this ensures that repeated deployments (with the same config) will have the
|
||||
same config, these changes should not be committed to source control.
|
||||
2. It will seek out dependencies that are listed in the object notation and try
|
||||
to install them from npm.
|
||||
|
||||
## Plugin Dependencies
|
||||
|
||||
You may also include additional external dependencies in your local packages by
|
||||
specifying a `package.json` at your plugin root which will result in a
|
||||
`node_modules` folder being generated at the plugin root with your specific
|
||||
dependencies.
|
||||
|
||||
## Deployment Solutions
|
||||
|
||||
Plugins can be deployed with a production instance of Talk.
|
||||
|
||||
### Source
|
||||
|
||||
Source deployments can just modify the `plugins.json` file and include any
|
||||
local plugins into the `plugins/` directory. After including the config, you
|
||||
need to reconcile the plugins and build the static assets:
|
||||
|
||||
```bash
|
||||
# get plugin dependancies and remote plugins
|
||||
./bin/cli plugins reconcile
|
||||
|
||||
# build staic assets (including enabled client side plugins)
|
||||
yarn build
|
||||
```
|
||||
|
||||
Then the application can be started as is.
|
||||
|
||||
### Docker
|
||||
|
||||
If you deploy using Docker, you can extend from the `*-onbuild` image, an
|
||||
example `Dockerfile` for your project could be:
|
||||
|
||||
```Dockerfile
|
||||
FROM coralproject/talk:latest-onbuild
|
||||
```
|
||||
|
||||
Where the directory for your instance would contain a `plugins.json` file
|
||||
describing the plugin requirements and a `plugins` directory containing any
|
||||
other local plugins that should be included.
|
||||
|
||||
Onbuild triggers will execute when the image is building with your custom
|
||||
configuration and will ensure that the image is ready to use by building all
|
||||
assets inside the image as well.
|
||||
@@ -0,0 +1,241 @@
|
||||
---
|
||||
title: Plugins Quickstart
|
||||
permalink: /docs/plugins/quickstart/
|
||||
---
|
||||
|
||||
This tutorial walks through the mechanics of creating and publishing a Talk plugin. Along the way I call out some particular habits and techniques that I employ. If you have other practices that you find valuable, please don't hesitate to [contribute!]({{ "/docs/faq" | absolute_url }}#how-do-i-contribute-to-these-docs)
|
||||
|
||||
We will create a plugin that exposes a route that allows assets to be created or updated.
|
||||
|
||||
## Setup the environment
|
||||
|
||||
Before I begin working on the plugin, I've installed [Talk from source]({{ "/docs/development/tools" | absolute_url }}).
|
||||
|
||||
### Watch the Server
|
||||
|
||||
In a terminal, I run `yarn dev-start`. This:
|
||||
|
||||
* starts my server, showing plugin and configuration information
|
||||
* restarts it when I save files
|
||||
* shows my temporary `console.log()` statements
|
||||
* shows real time access logs
|
||||
* shows verbose debug output if enabled (more on this later)
|
||||
|
||||
### Watch the Client build process
|
||||
|
||||
In a separate terminal I run `yarn build-watch`. This:
|
||||
|
||||
* builds the client side javascript bundles
|
||||
* watches relevant files and rebuilds the bundle on change
|
||||
* displays _compile time_ errors, including (the many) syntax errors I cause
|
||||
|
||||
If you need to run `yarn install`, you will see missing module error messages here.
|
||||
|
||||
### Watch from the Browser
|
||||
|
||||
I open up `http://localhost:3000` in a web browser and see the default comment stream. I then open the dev tools console, which:
|
||||
|
||||
* shows any _run time_ errors/warnings generated on the front end.
|
||||
* shows any temporary `console.log()` statements I add during development.
|
||||
|
||||
I also often toggle to the Network Tab to see:
|
||||
|
||||
* which files are being loaded
|
||||
* requests sent from my front end code, including headers, the payload/queries sent and the data returned
|
||||
|
||||
I strongly recommend taking the time to fully explore all the features of your browser's dev tools!
|
||||
|
||||
## Create a home for my new plugin
|
||||
|
||||
My goals for this tutorial are to:
|
||||
|
||||
* build this plugin locally
|
||||
* use source control and publish for collaboration
|
||||
* publish the plugin as an npm library
|
||||
|
||||
### Create a repo
|
||||
|
||||
I create a new repo called `talk-plugin-asset-manager`. (I use github, but this you could store this anywhere, bitbucket, svn, etc...)
|
||||
|
||||
_make sure to respect the naming convention `talk-plugin-*`. This will allow for easy identification of the repo and, eventually, easy searching on npm._
|
||||
|
||||
### Set up a local file structure
|
||||
|
||||
In a blatant rip off from the Golang community, I create an environment var to hold the path to the root of my Coral directory. This allows absolute pathing.
|
||||
|
||||
```
|
||||
export CORALPATH=/path/to/my/coral/root/dir
|
||||
```
|
||||
|
||||
I like to put my plugins in a directory next to talk, but you could put this anywhere.
|
||||
|
||||
```
|
||||
cd $CORALPATH
|
||||
git clone https://github.com/jde/talk-plugin-asset-manager.git
|
||||
```
|
||||
|
||||
### Register your plugin
|
||||
|
||||
Add the plugin to the plugins.json file:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": [
|
||||
...
|
||||
"talk-plugin-asset-manager"
|
||||
],
|
||||
"client": [
|
||||
...
|
||||
// no client side components so I won't add it here
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
But wait! Talk looks in `talk/plugins/[plugin-name]` for plugin code. Why couldn't we just add that plugin there?
|
||||
|
||||
We could have.
|
||||
|
||||
This would make it _a little_ easier to register, but _a lot_ harder to cleanly manage in version control. In order to avoid it being sucked into your Talk repo, you would have to manually `.gitignore` it or use sub modules or something similar.
|
||||
|
||||
As a user of a Linux_y_ os, I prefer to create a symbolic link.
|
||||
|
||||
```bash
|
||||
cd $CORALPATH/talk/plugins
|
||||
ln -s $CORALPATH/talk-plugin-asset-manager
|
||||
```
|
||||
|
||||
Now, as far as Talk knows, our plugin is right there in the folder. Git is wise, however, and will not include it in the Talk repo. Best of all, our `yarn dev-start` based watch statement follows symbolic links and will restart our sever each time a file is saved.
|
||||
|
||||
### Create the initial index file
|
||||
|
||||
All plugins contain server and/or client index files, which export all plugin functionality.
|
||||
|
||||
```js
|
||||
// talk-plugin-asset-manager/index.js
|
||||
module.exports = {};
|
||||
```
|
||||
|
||||
## Build the feature!
|
||||
|
||||
Now that the plugin is set up I can get down to writing the feature. My goal is to allow my CMS to push new assets as they are created into Talk. To accomplish this, I will create a POST endpoint using Talk's [route api]({{ "/docs/plugins/server" | absolute_url }}#field-router).
|
||||
|
||||
### Create a route
|
||||
|
||||
When designing my api, I want to be careful to avoid conflicts with not only the existing Talk api, but other plugins in the open source ecosystem that may be creating routes. To do this, I'll follow the golden rule of creating universals with plugins:
|
||||
|
||||
_Always namespace all universals with your plugin's unique name._
|
||||
|
||||
To ensure everything is hooked up, I'll log the request body (POST payload in this case) to the console and echo it as the response:
|
||||
|
||||
```js
|
||||
// talk-plugin-asset-manager/index.js
|
||||
module.exports = {
|
||||
router(router) {
|
||||
router.post('/api/v1/asset-manager', (req, res) => {
|
||||
console.log(req.body);
|
||||
res.json(req.body);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When I save this file, I reflexively check my console to be sure that the server restarts.
|
||||
|
||||
To test that this works, I can:
|
||||
|
||||
```bash
|
||||
$ curl -H "Content-Type: application/json" -X POST -d '{"url":"http://localhost:3000/my-article","title":"My Article"}' http://localhost:3000/api/v1/asset-manager
|
||||
{"url":"http://localhost:3000/my-article","title":"My Article"}
|
||||
```
|
||||
|
||||
After hitting the endpoint, I can also look at the terminal running `yarn dev-start` and see my `console.log()` and the access log:
|
||||
|
||||
```
|
||||
{ url: 'http://localhost:3000/my-article',
|
||||
title: 'My Article' }
|
||||
POST /api/v1/asset-manager 200 1.379 ms - 68
|
||||
```
|
||||
|
||||
### Save the asset
|
||||
|
||||
When I save this asset, I will use Talk's [asset model](https://github.com/coralproject/talk/blob/master/models/asset.js).
|
||||
|
||||
Mongo has a handy method [findOneAndUpdate](https://docs.mongodb.com/v3.2/reference/method/db.collection.findOneAndUpdate/) that will take care determining whether or not this asset exists, then either updating or inserting it. Whenever possible, we recommend using these atomic patterns that prevent multiple queries to the db and the efficiency problems and race conditions that they cause.
|
||||
|
||||
```js
|
||||
// talk-plugin-asset-manager/index.js
|
||||
|
||||
const AssetModel = require('models/asset');
|
||||
|
||||
module.exports = {
|
||||
router(router) {
|
||||
router.post('/api/v1/asset-manager', (req, res) => {
|
||||
|
||||
const asset = req.body;
|
||||
const update = {$setOnInsert: {url: asset.url}};
|
||||
|
||||
AssetModel.findOneAndUpdate(asset, update, {
|
||||
new: true,
|
||||
upsert: true,
|
||||
setDefaultsOnInsert: true
|
||||
})
|
||||
.then((asset) => res.json(asset));
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
I can now run the `curl` command as before and see that the response contains a complete asset document!
|
||||
|
||||
In addition, I can change the title in the json payload and verify that the id is the same, indicating that the record was updated!
|
||||
|
||||
Lastly, I can see the asset in the admin panel at `http://localhost:3000/admin` as well as in my database.
|
||||
|
||||
We have an alpha version of our plugin!
|
||||
|
||||
### More work to be done
|
||||
|
||||
The purpose of this tutorial is to follow the full lifecycle of a plugin, from conception through publication into deployment. With that in mind we'll move forward with this alpha version.
|
||||
|
||||
Some things to make this production ready:
|
||||
|
||||
* refactoring to separate concerns
|
||||
* commenting
|
||||
* adding tests
|
||||
* validating data
|
||||
* [adding security](https://github.com/coralproject/talk/blob/b805451d376d2892c81c58d8822a85563e469b88/routes/api/users/index.js#L14)
|
||||
* incorporating [domain whitelisting](https://github.com/coralproject/talk/blob/b805451d376d2892c81c58d8822a85563e469b88/services/assets.js#L60).
|
||||
|
||||
It is important to realize that when you're writing a Talk plugin you are writing a program that may be touched by other devs and could grow in size and complexity. Bring your best engineering sensibilities to bear.
|
||||
|
||||
## Publishing the plugin
|
||||
|
||||
### Publish to npm
|
||||
|
||||
In order to [register]({{ "/docs/plugins" | absolute_url }}#plugin-registration) your _published_ plugin, you will need to [publish it to npm](https://docs.npmjs.com/getting-started/publishing-npm-packages).
|
||||
|
||||
Once the package is published, update `plugins.json` to use the published plugin:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": [
|
||||
// ...
|
||||
{"talk-plugin-asset-manager": "^0.1"}
|
||||
],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, run the `reconcile` script to install the plugin from npm.
|
||||
|
||||
```bash
|
||||
$ bin/cli plugins reconcile
|
||||
```
|
||||
|
||||
Once you've taken this step, anyone can register your plugin into their Talk server! Thank you for contributing to the open source community!
|
||||
|
||||
### Publish to version control
|
||||
|
||||
This plugin is open source, so I'm also going to [publish it to github](https://github.com/jde/talk-plugin-asset-manager/commit/66b626caa85cb8030b3ddaa7c1a4821bf01e350a) and [cut a release](https://github.com/jde/talk-plugin-asset-manager/releases/tag/v0.1) that mirrors the npm relese.
|
||||
|
||||
## Done!
|
||||
@@ -0,0 +1,331 @@
|
||||
---
|
||||
title: Client Plugin API
|
||||
permalink: /docs/plugins/client/
|
||||
---
|
||||
|
||||
{% include toc title="API" %}
|
||||
|
||||
## Overview
|
||||
|
||||
We can build plugins to extend the client side functionality of Talk.
|
||||
|
||||
The ultimate goal of our client side plugin architecture is to allow developers
|
||||
to build on existing concepts without needing to understand core code while
|
||||
providing complete power and flexibility of javascript, css and html.
|
||||
|
||||
* [Plugin Architecture](#plugin-architecture)
|
||||
* Using our building block components
|
||||
* [Reactions](#reactions)
|
||||
* [Styling](#styling-plugins)
|
||||
|
||||
Advanced users will quickly realize that our plugins have complete access to core code. If you would like to write advanced plugins that reach outside of our published API as described in this document, please see [our notes on experimental plugins]({{ "/docs/plugins/experimental/" | absolute_url }}).
|
||||
|
||||
Under the hood our plugins are powered by *React*, *Redux* and *GraphQL*. We can also build them with simple vanilla javascript.
|
||||
|
||||
## Plugin Architecture
|
||||
|
||||
The plugins live in the `/plugins` folder. Each plugin must have an `index.js` file and two folders `client` and `server`.
|
||||
|
||||
### The Client Folder
|
||||
The frontend of our plugin lives inside the `client` folder. The `client` folder must have an `index.js` file that exports the configuration of our plugin.
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── client/
|
||||
│ └── index.js <-- index for client side functionality
|
||||
├── server/
|
||||
└── index.js <-- base plugin index
|
||||
```
|
||||
|
||||
For now our base plugin `index.js` file should look like this:
|
||||
|
||||
```js
|
||||
export default {
|
||||
// We will add more here later.
|
||||
};
|
||||
```
|
||||
|
||||
### Creating a Component
|
||||
|
||||
We can add our components (or any other javascript code) within the `client` folder.
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── client/
|
||||
│ ├── MyComponent.js
|
||||
│ └── index.js
|
||||
├── server/
|
||||
└── index.js
|
||||
```
|
||||
|
||||
Our component could look like this:
|
||||
|
||||
```js
|
||||
import React, {Component} from 'react';
|
||||
|
||||
class MyButton extends Component {
|
||||
render() {
|
||||
return <button>My Button</button>;
|
||||
}
|
||||
}
|
||||
|
||||
export default MyButton;
|
||||
```
|
||||
|
||||
Here we create a component that renders a `button`. Now that we created our component we need to specify where it should get injected within Talk!
|
||||
|
||||
To tell Talk where that Component should get injected we need to specify which *Slots* to insert it into.
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
export default = () => <button>My Button</button>;
|
||||
```
|
||||
|
||||
### Slots
|
||||
In Talk we have defined specific *Slots* where we can inject components.
|
||||
|
||||
Here is how we specify our slots config in `my-plugin/index.js`
|
||||
|
||||
```js
|
||||
import MyButton from './MyButton';
|
||||
|
||||
export default {
|
||||
slots: {
|
||||
commentDetail: [MyButton]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Here I’m specifying that the MyComponent Component will take place within the `commentDetail` in Talk.
|
||||
|
||||
`commentDetail` it’s a specific slot in the CommentStream. It means that it will be embedded inside de comment detail.
|
||||
|
||||
Slots properties take an`Array` so we can add as many components as we want.
|
||||
|
||||
## Building Blocks (TBD)
|
||||
|
||||
`Note: the concepts in this section are still to be implemented. Code samples are for discussion and may change.`
|
||||
|
||||
In order to allow you to build more complex plugins, we have wrapped some of our functionality in higher order components that expose a simple api.
|
||||
|
||||
## Reactions
|
||||
|
||||
Reactions provide users the ability to 'like', 'respect', etc... comments.
|
||||
|
||||
Note: some server side work will need to accompany this client side component. See the like and respect plugins as examples.
|
||||
|
||||
### Building Reactions
|
||||
|
||||
#### Our `client/index.js` :
|
||||
|
||||
```js
|
||||
import LoveButton from './LoveButton';
|
||||
|
||||
export default {
|
||||
slots: {
|
||||
commentReactions: [LoveButton]
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
In this example we add our reaction component to the `commentReaction` Slot
|
||||
|
||||
#### Our Reaction component:
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import {withReaction} from 'talk-plugin-api';
|
||||
|
||||
class LoveButton extends React.Component {
|
||||
handleClick = () => {
|
||||
const {
|
||||
postReaction,
|
||||
deleteReaction,
|
||||
alreadyReacted
|
||||
} = this.props;
|
||||
|
||||
if (alreadyReacted()) {
|
||||
deleteReaction();
|
||||
} else {
|
||||
postReaction();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {count} = this.props;
|
||||
return (
|
||||
<button onClick={this.handleClick}>
|
||||
<span>Love</span>
|
||||
<span>{count > 0 && count}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withReaction('love')(LoveButton);
|
||||
```
|
||||
|
||||
|
||||
This feature introduces `withReaction` HOC. `withReaction` takes, as argument, a reaction string and it allows our component to receive specific props for handling reactions.
|
||||
|
||||
* `postReaction` - Posts the reaction
|
||||
|
||||
* `deleteReaction` - Removes the reaction
|
||||
|
||||
* `alreadyReacted` - A function that returns a boolean.
|
||||
|
||||
* `count` - The reaction count
|
||||
|
||||
|
||||
For full reference: Please, check `talk-plugin-love`: [LoveButton.js](https://github.com/coralproject/talk/blob/master/plugins/talk-plugin-love/client/LoveButton.js)
|
||||
|
||||
### Comment Stream
|
||||
|
||||
Comment streams may be created with filtering and ordering in place:
|
||||
|
||||
* filter by user
|
||||
* filter by tag
|
||||
* sort by date ascending / descending
|
||||
|
||||
### Comment Commit hooks
|
||||
|
||||
// docs for the pre/post comment submit commit hooks
|
||||
|
||||
### Mod Queues
|
||||
|
||||
Moderation queues can be added via configuration objects passed in through plugins.
|
||||
|
||||
Basic mod queues will resemble the current moderation queues but can be generated from different lists of comments.
|
||||
|
||||
* filter by user tag
|
||||
* filter by comment tag
|
||||
* filter by comment status
|
||||
* Custom queries (paired with back end plugins that provide queries to get the data)
|
||||
|
||||
#### Advanced mod queues
|
||||
|
||||
Advanced mod queues can be created giving plugin authors the power to create the cards that appear in the queue, create actions and custom buttons, etc...
|
||||
|
||||
### Custom Configuration
|
||||
|
||||
Plugins may rely on configuration options that admins/moderators can set in the Configuration section.
|
||||
|
||||
Basic settings can be added via json configuration in a plugin.
|
||||
|
||||
* Setting headline
|
||||
* Setting description
|
||||
* Setting input type
|
||||
* Default value
|
||||
* Variable name
|
||||
|
||||
#### Advanced Custom Configuration (low prioritiy)
|
||||
|
||||
Users can inject configuration interfaces that they create into the configuration allowing for more advanced configuration.
|
||||
|
||||
|
||||
## Styling Plugins
|
||||
Talk uses CSS Modules. This basically means that you can also add your CSS Module to your plugin without colliding with the rest of Talk!
|
||||
|
||||
##### My Component
|
||||
```js
|
||||
import styles from './style.css';
|
||||
|
||||
class MyCoralButton extends Component {
|
||||
render() {
|
||||
return <button className={styles.button}>My Button</button>;
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
Our `style.css` should could look like this.
|
||||
```css
|
||||
|
||||
.button {
|
||||
background: coral;
|
||||
border-radius: 3px;
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin Hooks
|
||||
The plugins injected in the CommentBox such as `commentInputDetailArea` will inherit through props tools for handling hooks.
|
||||
|
||||
### Available hook types:
|
||||
`preSubmit` : To perform actions before submitting the comment.
|
||||
`postSubmit` : To perform actions after submitting the comment.
|
||||
|
||||
### Register Hooks
|
||||
`registerHook` is a function that takes: the hook type, a hook function and returns the hook data.
|
||||
|
||||
#### Usage:
|
||||
```js
|
||||
this.addCommentTagHook = this.props.registerHook('postSubmit', (data) => {
|
||||
const {comment} = data.createComment;
|
||||
this.props.addCommentTag({
|
||||
id: comment.id,
|
||||
tag: 'OFF_TOPIC'
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Unregister Hooks
|
||||
|
||||
`unregisterHook` will remove the hook.
|
||||
|
||||
```js
|
||||
this.props.unregisterHook(this.addCommentTagHook);
|
||||
```
|
||||
|
||||
### The server folder and the index file
|
||||
|
||||
Read more about the `/server` and how to extend Talk here.
|
||||
|
||||
[Server API]({{ "/docs/plugins/server/" | absolute_url }})
|
||||
|
||||
## ESlint and Babel
|
||||
In talk we use `eslint:recommended` and Babel with the latest ECMAScript Features. But you can use your own!
|
||||
While building your plugin you need to specify a `.eslintrc.json` file and a`.babelrc` file.
|
||||
|
||||
### `.eslintrc.json`
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"mocha": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true,
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"rules": {
|
||||
"react/jsx-uses-react": "error",
|
||||
"react/jsx-uses-vars": "error",
|
||||
"no-console": ["warn", { "allow": ["warn", "error"] }]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `.babelrc `
|
||||
```json
|
||||
{
|
||||
"presets": [
|
||||
"es2015"
|
||||
],
|
||||
"plugins": [
|
||||
"add-module-exports",
|
||||
"transform-class-properties",
|
||||
"transform-decorators-legacy",
|
||||
"transform-object-assign",
|
||||
"transform-object-rest-spread",
|
||||
"transform-async-to-generator",
|
||||
"transform-react-jsx"
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,448 @@
|
||||
---
|
||||
title: Server Plugin API
|
||||
permalink: /docs/plugins/server/
|
||||
---
|
||||
|
||||
{% include toc title="API" %}
|
||||
|
||||
## The Server Folder
|
||||
The server functionality of our plugin lives inside the `server` folder. The `server`
|
||||
folder must have an `index.js` file that exports the configuration of our plugin.
|
||||
|
||||
```
|
||||
my-plugin/
|
||||
├── client/
|
||||
│ └── ... <-- client side plugin files
|
||||
├── server/
|
||||
│ └── index.js <-- index for server side functionality
|
||||
│ └── ... <-- other server side plugin files
|
||||
└── index.js <-- base plugin index
|
||||
```
|
||||
|
||||
## Specification
|
||||
|
||||
Each plugin should export a single object with all hooks available on it.
|
||||
|
||||
_**Note: You will have access to the whole core and other plugin's typeDefs,
|
||||
context, loaders, mutators, resolvers, hooks. This is intentional, as it
|
||||
encourages composing plugins to merge functionality, like a Slack plugin which
|
||||
provides a Slack notify context function as well as having the loader for
|
||||
comments.**_
|
||||
|
||||
The following are the hooks available:
|
||||
|
||||
### GraphQL hooks
|
||||
|
||||
#### Field: `typeDefs`
|
||||
|
||||
```graphql
|
||||
enum COLOUR {
|
||||
RED
|
||||
BLUE
|
||||
}
|
||||
|
||||
type Person {
|
||||
name: String!
|
||||
colour: COLOUR!
|
||||
}
|
||||
|
||||
type RootMutation {
|
||||
createPerson(name: String!): Person
|
||||
}
|
||||
|
||||
type RootQuery {
|
||||
people: [Person!]
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
leader: Person
|
||||
}
|
||||
```
|
||||
|
||||
Thanks to [gql-merge](https://www.npmjs.com/package/gql-merge) the contents of
|
||||
`typeDefs` should be a string that will be _merged_ with the existing type
|
||||
definitions. `enum`'s will be appended to, types will be appended, and new types
|
||||
will be added.
|
||||
|
||||
#### Field: `context`
|
||||
|
||||
```js
|
||||
{
|
||||
Slack: (context) => ({
|
||||
notify: (message) => {
|
||||
// return a promise after we're done sending notifications.
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Any property provided here will be added to the context parameter available
|
||||
inside all resolvers, loaders, mutators, and of course, other context based
|
||||
plugins.
|
||||
|
||||
The top level item must accept a context for the request which it should use to
|
||||
configure the context plugin before it would be mounted at `context.plugins`.
|
||||
This plugin above would mount at: `context.plugins.Slack`, or, if you're using
|
||||
[object destructuring](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment), `{plugins: {Slack}}`.
|
||||
|
||||
#### Field: `loaders`
|
||||
|
||||
```js
|
||||
(context) => ({
|
||||
People: {
|
||||
load: () => db.people.find({user: context.user})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Loaders should be provided as a function which returns a map which is used in
|
||||
the resolvers function. These must return a promise or a value.
|
||||
|
||||
#### Field: `mutators`
|
||||
|
||||
```js
|
||||
(context) => ({
|
||||
People: {
|
||||
create: (name) => {
|
||||
return db.people.insert({user: context.user, name});
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Mutators should be provided as a function which returns a map which is used in
|
||||
the resolvers function. These must return a promise or a value.
|
||||
|
||||
#### Field: `resolvers`
|
||||
|
||||
```js
|
||||
{
|
||||
Person: {
|
||||
name(obj, args, context) {
|
||||
return obj.name;
|
||||
},
|
||||
colour(obj, args, context) {
|
||||
// Bill likes the colour red, everyone else likes blue.
|
||||
return obj.name === 'bill' ? 'RED' : 'BLUE';
|
||||
}
|
||||
},
|
||||
RootQuery: {
|
||||
people(obj, args, {loaders: {People}}) {
|
||||
return People.load();
|
||||
}
|
||||
},
|
||||
RootMutation: {
|
||||
createPerson(obj, {name}, {mutators: {People}}) {
|
||||
return People.create(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Should return a resolver map as described in the
|
||||
[Apollo Docs](http://dev.apollodata.com/tools/graphql-tools/resolvers#Resolver-map).
|
||||
|
||||
This will merge with the existing resolvers in core and from previous plugins.
|
||||
|
||||
#### Field: `hooks`
|
||||
|
||||
```js
|
||||
{
|
||||
RootMutation: {
|
||||
createPerson: {
|
||||
post: async (obj, args, {plugins: {Slack}}, info, person) {
|
||||
if (!person) {
|
||||
return person;
|
||||
}
|
||||
|
||||
await Slack.notify(`A new person just was created with name ${person.name}`);
|
||||
|
||||
return person;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Hooks here are pretty special, for each resolver field, you can specify a
|
||||
pre/post hook that will execute pre and post field resolution.
|
||||
|
||||
If your post function accepts four parameters, then it can modify the field
|
||||
result. It is *required* that the function resolves a promise (or returns) with
|
||||
the modified value or simply the original if you didn't modify it.
|
||||
|
||||
#### Field: `setupFunctions`
|
||||
|
||||
```js
|
||||
setupFunctions: {
|
||||
leader: (options, args) => ({
|
||||
leader: {
|
||||
filter: (person) => person.place === 1
|
||||
},
|
||||
}),
|
||||
}
|
||||
```
|
||||
|
||||
Setup functions allow you to create filters that control which pubsub.publish() events
|
||||
send data to the client. If the type in question contains args, clients may subscribe using those arguments to further filter their subscription.
|
||||
|
||||
For more information, see the [Apollo Docs](https://github.com/apollographql/graphql-subscriptions).
|
||||
|
||||
#### Field: `tokenUserNotFound`
|
||||
|
||||
```js
|
||||
tokenUserNotFound: async ({jwt, token}) => {
|
||||
let profile = await someExternalService(token);
|
||||
if (!profile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let user = await UserModel.findOneAndUpdate({
|
||||
id: profile.id
|
||||
}, {
|
||||
id: profile.id,
|
||||
username: profile.username,
|
||||
lowercaseUsername: profile.username.toLowerCase(),
|
||||
roles: [],
|
||||
profiles: []
|
||||
}, {
|
||||
setDefaultsOnInsert: true,
|
||||
new: true,
|
||||
upsert: true
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
```
|
||||
|
||||
The `tokenUserNotFound` hook allows auth integrations to hook into the event
|
||||
when a valid token is provided but a user can't be found in the database that
|
||||
matches the provided id.
|
||||
|
||||
The function is async, and should return the user object that was created in the
|
||||
database, or null if the user wasn't found. The `jwt` paramenter of the object
|
||||
is the unpacked token, while `token` is the original jwt token string.
|
||||
|
||||
### Routes
|
||||
|
||||
#### Field: `router`
|
||||
|
||||
```js
|
||||
(router) => {
|
||||
router.get('/api/v1/people', (req, res) => {
|
||||
res.json({people: [{name: 'Bob'}]});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The Router hook allows you to create a function that accepts the base express
|
||||
router where you can mount any amount of middleware/routes to do any form of
|
||||
action needed by external applications.
|
||||
|
||||
#### Field: `tags`
|
||||
|
||||
The tags hook allows a plugin to define tags that are code controlled (added
|
||||
or enabled by code). Below is an example pulled from the core off topic plugin
|
||||
on how to create a hook for the `OFF_TOPIC` name:
|
||||
|
||||
```js
|
||||
[
|
||||
{
|
||||
name: 'OFF_TOPIC',
|
||||
permissions: {
|
||||
public: true,
|
||||
self: true,
|
||||
roles: []
|
||||
},
|
||||
models: ['COMMENTS'],
|
||||
created_at: new Date()
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
You can refer to `models/schema/tag.js` for the available schema to match when
|
||||
creating models to enable/disable specific features.
|
||||
|
||||
### Authorization middleware
|
||||
|
||||
The following example creates the requisite callback route and passport
|
||||
strategy needed to enable Facebook Authorization:
|
||||
|
||||
```js
|
||||
const authorization = require('middleware/authorization');
|
||||
|
||||
module.exports = {
|
||||
router(router) {
|
||||
router.get('/api/v1/people', authorization.needed('ADMIN'), (req, res) => {
|
||||
res.json({people: [{name: 'SECRET PEOPLE'}]});
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Field: `passport`
|
||||
|
||||
```js
|
||||
const FacebookStrategy = require('passport-facebook').Strategy;
|
||||
const UsersService = require('services/users');
|
||||
const {ValidateUserLogin, HandleAuthPopupCallback} = require('services/passport');
|
||||
|
||||
module.exports = {
|
||||
passport(passport) {
|
||||
passport.use(new FacebookStrategy({
|
||||
clientID: process.env.TALK_FACEBOOK_APP_ID,
|
||||
clientSecret: process.env.TALK_FACEBOOK_APP_SECRET,
|
||||
callbackURL: `${process.env.TALK_ROOT_URL}/api/v1/auth/facebook/callback`,
|
||||
passReqToCallback: true,
|
||||
profileFields: ['id', 'displayName', 'picture.type(large)']
|
||||
}, async (req, accessToken, refreshToken, profile, done) => {
|
||||
|
||||
let user;
|
||||
try {
|
||||
user = await UsersService.findOrCreateExternalUser(profile);
|
||||
} catch (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
return ValidateUserLogin(profile, user, done);
|
||||
}));
|
||||
},
|
||||
router(router) {
|
||||
|
||||
// Note that we have to import the passport instance here, it is
|
||||
// instantiated after all the strategies have been mounted.
|
||||
const {passport} = require('services/passport');
|
||||
|
||||
/**
|
||||
* Facebook auth endpoint, this will redirect the user immediatly to facebook
|
||||
* for authorization.
|
||||
*/
|
||||
router.get('/facebook', passport.authenticate('facebook', {display: 'popup', authType: 'rerequest', scope: ['public_profile']}));
|
||||
|
||||
/**
|
||||
* Facebook callback endpoint, this will send the user a html page designed to
|
||||
* send back the user credentials upon sucesfull login.
|
||||
*/
|
||||
router.get('/facebook/callback', (req, res, next) => {
|
||||
|
||||
// Perform the facebook login flow and pass the data back through the opener.
|
||||
passport.authenticate('facebook', HandleAuthPopupCallback(req, res, next))(req, res, next);
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Full Example
|
||||
|
||||
Contents of `plugins.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": [
|
||||
"people"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Located in `plugins/people/index.js`:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
typeDefs: `
|
||||
enum COLOUR {
|
||||
RED
|
||||
BLUE
|
||||
}
|
||||
|
||||
type Person {
|
||||
name: String!
|
||||
colour: COLOUR!
|
||||
}
|
||||
|
||||
type RootMutation {
|
||||
createPerson(name: String!): Person
|
||||
}
|
||||
|
||||
type RootQuery {
|
||||
people: [Person!]
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
leader: Person
|
||||
}
|
||||
`,
|
||||
context: {
|
||||
Slack: () => ({
|
||||
notify: (message) => {
|
||||
// return a promise after we're done sending notifications.
|
||||
}
|
||||
})
|
||||
},
|
||||
loaders: ({user}) => ({
|
||||
People: {
|
||||
load: () => db.people.find({user})
|
||||
}
|
||||
}),
|
||||
mutators: ({user}) => ({
|
||||
People: {
|
||||
create: (name) => {
|
||||
return db.people.insert({user, name});
|
||||
}
|
||||
}
|
||||
}),
|
||||
resolvers: {
|
||||
Person: {
|
||||
name(obj, args, context) {
|
||||
return obj.name;
|
||||
},
|
||||
colour(obj, args, context) {
|
||||
// Bill likes the colour red, everyone else likes blue.
|
||||
return obj.name === 'bill' ? 'RED' : 'BLUE';
|
||||
}
|
||||
},
|
||||
RootQuery: {
|
||||
people(obj, args, {loaders: {People}}) {
|
||||
return People.load();
|
||||
}
|
||||
},
|
||||
RootMutation: {
|
||||
createPerson(obj, {name}, {mutators: {People}}) {
|
||||
return People.create(name);
|
||||
}
|
||||
}
|
||||
},
|
||||
hooks: {
|
||||
RootMutation: {
|
||||
createPerson: {
|
||||
post: async (obj, args, {plugins: {Slack}}, info, person) => {
|
||||
if (!person) {
|
||||
return person;
|
||||
}
|
||||
|
||||
await Slack.notify(`A new person just was created with name ${person.name}`);
|
||||
|
||||
return person;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
setupFunctions: {
|
||||
leader: (options, args) => ({
|
||||
leader: {
|
||||
filter: (person) => person.place === 1
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
You can access any API available inside the talk directory in a plugin by simply
|
||||
importing the file relative to the talk project root. An example would be if you
|
||||
wanted to import the `MetadataService`, you would simply write:
|
||||
|
||||
```javascript
|
||||
const MetadataService = require('services/metadata');
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: Experimental Plugin API
|
||||
permalink: /docs/plugins/experimental/
|
||||
---
|
||||
|
||||
Talk plugins are, in essence, small programs that hook into the core application in a variety of ways. Ultimately, this code can do anything that javascript is capable of. In addition, plugins can import any core code to hook into talk at any level.
|
||||
|
||||
If you want to write plugins that integrate with core code beyond the api described
|
||||
in the client api or server api section, please keep the following things in mind:
|
||||
|
||||
* core code may change and break your plugin
|
||||
* you may introduce inefficiencies with your plugin that could hurt performance/
|
||||
crash Talk
|
||||
* you may cause bugs in other areas of Talk
|
||||
|
||||
If you'd like to build a supported plugin but don't have the hooks you need,
|
||||
please file an issue on this repo and we can discuss deepening the supported
|
||||
plugin api!
|
||||
|
||||
With that said, if you are still undeterred, here are some of the prime
|
||||
experimental integration points:
|
||||
|
||||
## Reducers and Actions : Redux
|
||||
|
||||
Talk is powered by Redux and our plugins can too! Our plugins can have their own reducers and actions.
|
||||
|
||||
```js
|
||||
import MyButton from './MyButton';
|
||||
import reducer from './reducer';
|
||||
|
||||
export default {
|
||||
slots: {
|
||||
commentDetail: [MyButton],
|
||||
},
|
||||
reducer
|
||||
};
|
||||
```
|
||||
|
||||
## Import Actions from Talk
|
||||
We can easily trigger `Talk` actions in our plugin Components.
|
||||
|
||||
```js
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {addTag, removeTag} from 'talk-plugin-commentbox/actions';
|
||||
|
||||
class MyButton extends Component {
|
||||
render() {
|
||||
return <button onClick={this.props.addTag('MY_TAG')}>My Button</button>;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({commentBox}) => ({commentBox});
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({addTag, removeTag}, dispatch);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(OffTopicCheckbox);
|
||||
```
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Development Tooling
|
||||
permalink: /docs/development/tools
|
||||
---
|
||||
|
||||
## Debugging
|
||||
How we debug errors at Coral
|
||||
|
||||
### React Debugging
|
||||
For debugging React
|
||||
|
||||
#### React Developer Tools
|
||||
Another amazing tool for debugging React Applications. You can see where the props are, and much more.
|
||||
|
||||
[React Developer Tools Extension](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en)
|
||||
|
||||
|
||||
### Redux Debugging
|
||||
For debugging Redux
|
||||
|
||||
#### Redux Devtool Extension
|
||||
Redux Devtool is an amazing debug tool. You can easily see what' happening with the state, the payloads, and more.
|
||||
|
||||
[Redux Devtool Chrome Extension](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en)
|
||||
|
||||
[Redux Devtool Github Repo](https://github.com/zalmoxisus/redux-devtools-extension)
|
||||
|
||||
### GraphQL
|
||||
|
||||
Talk ships with GraphiQL, a live data layer IDE. We strongly recommend using GraphiQL early and often as you work with all things queries, mutations and subscriptions!
|
||||
|
||||
* [Install Talk]({{ "/docs/install/source" | absolute_url }}).
|
||||
* Open http://localhost:3000/api/v1/graph/iql in your browser. (Note, your server an port may differ.)
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Contributor's Guide
|
||||
permalink: /docs/development/contributing/
|
||||
---
|
||||
|
||||
Welcome! We are very excited that you are interested in contributing to Talk.
|
||||
|
||||
This document is a companion to help you approach contributing. If it does not do so, please [let us know how we can improve it](https://github.com/coralproject/talk/issues)!
|
||||
|
||||
By contributing to this project you agree to the [Code of Conduct](https://coralproject.net/code-of-conduct).
|
||||
|
||||
## What should I Contribute?
|
||||
|
||||
There are at least three ways to contribute to Talk:
|
||||
|
||||
* Writing Code
|
||||
* Writing Docs
|
||||
* Providing Translations
|
||||
|
||||
## Writing Code
|
||||
|
||||
Conversation surrounding contributions begins in [issues](https://github.com/coralproject/talk/issues).
|
||||
|
||||
### When should I Create an Issue?
|
||||
|
||||
File an issue as soon as you have an idea of something you'd like to contribute. We would love to hear what you're thinking and help refine the idea to make it into the Talk ecosystem.
|
||||
|
||||
Please file issues if:
|
||||
|
||||
* you would like to contribute to Talk Core.
|
||||
* you are building a Plugin that the current Plugin API doesn't support.
|
||||
* you are building a Plugin and want advice.
|
||||
|
||||
### What should I include?
|
||||
|
||||
Coral has adopted an iterative, agile development philosophy. All contributions that make it into the Talk repo should start with a story or this form:
|
||||
|
||||
> As a [type of person] I'd like to be able to [do something] so that I can [get some result].
|
||||
|
||||
This exercise does two things:
|
||||
|
||||
* allows us to ground our technical choices in a clear, simple product need.
|
||||
* expresses that product need in a way that doesn't imply a specific technical solution allowing for debate as to the best way to solve the problem.
|
||||
|
||||
Please feel free to provide as much detail as possible when filing the issue but please do keep the initial issue specific to one need and try to avoid including technical or design solutions.
|
||||
|
||||
If you have a specific technical or design solution in mind, please submit it as the first comment on the thread.
|
||||
|
||||
## Contributing Documentation
|
||||
|
||||
Clear docs are a prerequisite for a successful open source project. Contributing to the documentation is often more important than contributing to the code!
|
||||
|
||||
We are looking for _documentarians_ to:
|
||||
|
||||
* make clarity, grammar and completeness updates,
|
||||
* create new / missing sections, and
|
||||
* take the lead in making sections, or the over all structure better.
|
||||
|
||||
Information about how to update docs can be found in our [FAQ]({{ "/docs/faq" | absolute_url }}#how-do-i-contribute-to-these-docs).
|
||||
|
||||
If you'd like to discuss a contribution, please [file an issue](https://github.com/coralproject/talk/issues) describing the changes you would like to see.
|
||||
|
||||
## Contributing Translations
|
||||
|
||||
Talk's tranlations are stored in `.yml` files [here](https://github.com/coralproject/talk/tree/master/locales).
|
||||
|
||||
Translations can be submitted via pull request. If you do not use github, you can use 'en.yml' as a template and [email](https://coralproject.net/contact) the translations to us. We can import it into the repo.
|
||||
|
||||
## I want to contribute but I'm not sure what to do!
|
||||
|
||||
If you want to contribute but don't have a clear idea of exactly what that may be, here are some resources that may help:
|
||||
|
||||
### Product Roadmap
|
||||
|
||||
Please visit our product roadmap here: https://www.pivotaltracker.com/n/projects/1863625. If you'd like to take on any of our scheduled tasks we'd be forever grateful!
|
||||
|
||||
### Discussion Forum
|
||||
|
||||
If you'd like to discuss what we're up to, please visit or [community](https://community.coralproject.net/) or [contact us](https://coralproject.net/contact).
|
||||
|
||||
### Integrations
|
||||
|
||||
Have a favorite analytics engine? Data science service? CMS? Auth platform? Deployment platform or pipeline? Pet project? Consider building a plugin to integrate them!
|
||||
|
||||
### Favorite Features?
|
||||
|
||||
Do you have a favorite feature of an existing platform that's not yet been done in Talk? Sounds like Talk needs that feature.
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Contributor Covenant Code of Conduct
|
||||
permalink: /docs/development/code-of-conduct/
|
||||
---
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We expect everyone contributing to The Coral Project to follow this code of conduct. That means the team, contractors we employ, contributors, as well as anyone posting to our public or internal-facing channels.
|
||||
We created it not because we anticipate any unacceptable behavior, but because we believe that articulating our values and obligations to one another reinforces the already exceptional level of respect among the team, and because having a code provides us with clear avenues to correct our culture should it ever stray from that course.
|
||||
|
||||
We commit to enforce and evolve this code over the duration of the project.
|
||||
|
||||
## Expected behavior
|
||||
|
||||
* Be supportive of each other.
|
||||
* Be collaborative. Involve others in brainstorms, sketching sessions, code reviews, planning documents, and the like. It’s not only okay to ask for help or feedback often, it’s unacceptable not to do so.
|
||||
* Be generous and kind in both giving and accepting critique. Critique is a natural and important part of our culture. Good critiques are kind, respectful, clear, and constructive, focused on goals and requirements rather than personal preferences. You are expected to give and receive criticism with grace.
|
||||
* Be humane. Be polite and friendly in all forms of communication, especially remote communication, where opportunities for misunderstanding are greater. Use sarcasm carefully. Tone is hard to decipher online; make judicious use of emoji to aid in communication.
|
||||
* Be considerate.
|
||||
* Be tolerant.
|
||||
* Respect people’s boundaries.
|
||||
* Do not make it personal.
|
||||
* Use welcoming and inclusive language.
|
||||
* Offer to help if you see someone struggling or otherwise in need of assistance (taking care not to be patronizing or disrespectful).
|
||||
* If someone approaches you looking for help, be generous with your time; if you’re under a deadline, direct them to someone else who may be of assistance.
|
||||
* Go out of your way to include people in jokes or memes, recognizing that we want to build an environment free of cliques.
|
||||
* Show empathy towards other community members
|
||||
|
||||
|
||||
## Unacceptable behavior
|
||||
|
||||
We are committed to providing a welcoming and safe environment for people of all races, gender identities, gender expressions, sexual orientations, physical abilities, physical appearances, socioeconomic backgrounds, nationalities, ages, religions, and beliefs.
|
||||
We expect that you will refrain from demeaning, discriminatory, or harassing behavior and speech.
|
||||
|
||||
Harassment includes, but is not limited to: deliberate intimidation; stalking; unwanted photography or recording; sustained or willful disruption of talks or other events; inappropriate physical contact; use of sexual or discriminatory imagery, comments, or jokes; and unwelcome sexual attention.
|
||||
Furthermore, any behavior or language which is unwelcoming—whether or not it rises to the level of harassment—is also strongly discouraged. Much exclusionary behavior takes the form of microaggressions—subtle put-downs which may be unconsciously delivered. Regardless of intent, microaggressions can have a significant negative impact on victims and have no place on our team.
|
||||
|
||||
Other inappropriate behavior:
|
||||
|
||||
* Threats
|
||||
* Slurs
|
||||
* Pornography
|
||||
* Spam
|
||||
* Bullying
|
||||
* Copyright infringement
|
||||
* Impersonation of someone else
|
||||
* Violating someone’s privacy
|
||||
|
||||
If you feel that someone has harassed you or otherwise treated you or someone else inappropriately, please alert the project lead at [andrewl@mozillafoundation.org](mailto:andrewl@mozillafoundation.org).
|
||||
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
These guidelines are ambitious, and we’re not always going to succeed in meeting them. When something goes wrong—whether it’s a microaggression or an instance of harassment — there are a number of things you can do to address the situation. Depending on your comfort level and the severity of the situation, here are some suggestions:
|
||||
|
||||
* Address it directly. If you’re comfortable bringing up the incident with the person who instigated it, pull them aside to discuss how it affected you. Be sure to approach these conversations in a forgiving spirit: an angry or tense conversation will not do either of you any good. If you’re unsure how to go about that, try discussing with your manager or with the people and culture team first—they might have some advice about how to make this conversation happen.
|
||||
|
||||
If you’re too frustrated to have a direct conversation, there are a number of alternate routes you can take.
|
||||
|
||||
* Talk to a peer or mentor. Your colleagues are likely to have personal and professional experience on which to draw that could be of use to you. If you have someone you’re comfortable approaching, reach out and discuss the situation with them. They may be able to advise on how they would handle it, or direct you to someone who can. The flip side of this, of course, is that you should also be available when your colleagues reach out to you.
|
||||
|
||||
* Contact the project lead, Andrew Losowsky, [andrewl@mozillafoundation.org](mailto:andrewl@mozillafoundation.org), or the technical lead. We will work with you to help you figure out how to ensure that any conflict doesn’t interfere with your work, in confidence if you would prefer.
|
||||
|
||||
* Talk to Chris Lawrence. Chris oversees the project. He can be contacted at [clawrence@mozillafoundation.org](mailto:clawrence@mozillafoundation.org).
|
||||
|
||||
If you feel you have been unfairly accused of violating this code of conduct, you should contact Chris with a concise description of your grievance.
|
||||
|
||||
## Conclusion
|
||||
|
||||
We welcome your feedback on this and every other aspect of what we do as The Coral Project, and we thank you for working with us to make it a safe, enjoyable, and friendly experience for everyone involved in the project and what we do.
|
||||
Above text is licensed CC BY-SA 4.0, adapted from the SRCCON code of conduct, FreeBSD’s code of conduct, Vox Media’s product team code of conduct, Medium’s code of conduct, as well as adapted from the Contributor Covenant.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: REST API
|
||||
permalink: /docs/development/rest/
|
||||
---
|
||||
|
||||
Talk provides REST API documentation at [swagger.yaml]({{ "swagger.yaml" | absolute_url }}).
|
||||
Reference in New Issue
Block a user