Fix merge conflicts

This commit is contained in:
Kim Gardner
2017-07-26 21:49:50 -04:00
286 changed files with 19785 additions and 12945 deletions
+67
View File
@@ -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.
+21
View File
@@ -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)!
+145
View File
@@ -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
```
+78
View File
@@ -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
+36
View File
@@ -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._
+95
View File
@@ -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`).
+98
View File
@@ -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`)
+106
View File
@@ -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>"}]
```
+15
View File
@@ -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.
+77
View File
@@ -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.
+68
View File
@@ -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.
+89
View File
@@ -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).
+136
View File
@@ -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).
+126
View File
@@ -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 dont 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)
+106
View File
@@ -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.
+241
View File
@@ -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!
+331
View File
@@ -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 Im specifying that the MyComponent Component will take place within the `commentDetail` in Talk.
`commentDetail` its 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"
]
}
```
+448
View File
@@ -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');
```
+60
View File
@@ -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);
```
+33
View File
@@ -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.)
+87
View File
@@ -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.
+81
View File
@@ -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. Its not only okay to ask for help or feedback often, its 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 peoples 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 youre 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 someones 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 were not always going to succeed in meeting them. When something goes wrong—whether its 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 youre 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 youre 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 youre 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 youre 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 doesnt 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, FreeBSDs code of conduct, Vox Medias product team code of conduct, Mediums code of conduct, as well as adapted from the Contributor Covenant.
+6
View File
@@ -0,0 +1,6 @@
---
title: REST API
permalink: /docs/development/rest/
---
Talk provides REST API documentation at [swagger.yaml]({{ "swagger.yaml" | absolute_url }}).