Remove docs from repo ✂✂✂ (#14579)

* Remove docs ✂✂✂✂

* Remove all references to docs and gitdocs

* Update docs.forem.com to developers.forem.com

* Remove .gitdocs_build/

* Update yarn.lock
This commit is contained in:
Andy Zhao 2021-08-23 17:23:42 -04:00 committed by GitHub
parent 32cb3b3408
commit 3b81172c56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
125 changed files with 60 additions and 11914 deletions

View file

@ -38,7 +38,6 @@ node_modules/
/app/assets/javascripts/generated/*
latest.dump
.byebug_history
.gitdocs_build/
# Ignore application configuration
/public/packs

View file

@ -1,28 +0,0 @@
/*
* This file is created so that it's possible to
* Run `gitdocs serve` from root
*/
var config = require('./docs/.gitdocs.json');
module.exports = new Promise((resolve, reject) => {
config.root = 'docs/';
setupHost(config);
resolve(config);
});
function fetchAppDomain() {
const { execSync } = require('child_process');
const appDomainGetCmd =
'rails runner "puts ApplicationConfig.app_domain_no_port"';
return execSync(appDomainGetCmd).toString().replace(/\s/g, '');
}
function setupHost(config) {
const appDomain = fetchAppDomain();
if (config.host === '0.0.0.0' && appDomain) {
config.host = appDomain;
}
}

View file

@ -41,7 +41,7 @@ images for UI changes._
_If your PR includes UI changes, please replace this line with details on how
accessibility is impacted and tested. For more info, check out the
[Forem Accessibility Docs](https://docs.forem.com/frontend/accessibility)._
[Forem Accessibility Docs](https://developers.forem.com/frontend/accessibility)._
## Added/updated tests?
@ -56,7 +56,7 @@ _Will this PR introduce a change that impacts Forem members or creators, the
development process, or any of our internal teams? If so, please note how you
will share this change with the people who need to know about it._
- [ ] I've updated the [Developer Docs](https://docs.forem.com) and/or
- [ ] I've updated the [Developer Docs](https://developers.forem.com) and/or
[Admin Guide](https://admin.forem.com/), or
[Storybook](https://storybook.forem.com/) (for Crayons components)
- [ ] I've updated the README or added inline documentation

6
.gitignore vendored
View file

@ -32,7 +32,6 @@ node_modules/
/app/assets/javascripts/generated/*
latest.dump
.byebug_history
.gitdocs_build/
# Ignore application configuration
/config/application.yml
@ -64,11 +63,6 @@ package-lock.json
# Development Docker storage location
_docker-storage/
# YARD generated doc (in the Gitdocs folder)
docs/.static/ruby-doc/
# ReDoc generated API doc (in the Gitdocs folder)
docs/.static/api/
# AWS Cloud9 specific folder for user settings
.c9/

View file

@ -100,7 +100,6 @@ Layout/LineLength:
Description: 'Checks that line length does not exceed the configured limit.'
AutoCorrect: true # this is false by default
Exclude:
- docs/Gemfile
- Gemfile
- app/lib/constants/site_config.rb

View file

@ -7,7 +7,6 @@
.eslintignore
.eslintrc.js
.gitattributes
.gitdocs.js
.github/
.gitpod.dockerfile
.gitpod.yml
@ -40,7 +39,6 @@ cypress.dev.json
cypress.json
cypress/
docker-compose.yml
docs/
empty-module.js
jest.config.js
postcss_error.log

View file

@ -19,4 +19,4 @@ We are all humans trying to work together to improve the community. Always be
kind and appreciate the need for trade-offs. ❤️
Before making your first pull request or issue, give our full
[Contributor's Guide](https://docs.forem.com/contributing/forem/) a read.
[Contributor's Guide](https://developers.forem.com/contributing/forem/) a read.

View file

@ -94,14 +94,14 @@ proceed.
## Getting Started
This section provides a high-level quick start guide. If you're looking for the
[installation guide](https://docs.forem.com/installation/), you'll want to refer
to our complete [Developer Documentation](https://docs.forem.com/).
[installation guide](https://developers.forem.com/installation/), you'll want to
refer to our complete [Developer Documentation](https://developers.forem.com/).
We run on a [Rails](https://rubyonrails.org/) backend, and we are currently
transitioning to a [Preact](https://preactjs.com/)-first frontend.
A more complete overview of our stack is available in
[our docs](https://docs.forem.com/technical-overview/).
[our docs](https://developers.forem.com/technical-overview/).
### Prerequisites
@ -130,11 +130,11 @@ A more complete overview of our stack is available in
### Installation Documentation
[View Full Installation Documentation](https://docs.forem.com/installation/).
[View Full Installation Documentation](https://developers.forem.com/installation/).
## Developer Documentation
[Check out our dedicated docs page for more technical documentation](https://docs.forem.com).
[Check out our dedicated docs page for more technical documentation](https://developers.forem.com).
## Core team
@ -192,7 +192,7 @@ Agreement, which they generously made available to the public domain under
Creative Commons CC0 1.0 Universal.
Any questions, please refer to our
[license FAQ](https://docs.forem.com/licensing/) doc or email yo@dev.to.
[license FAQ](https://developers.forem.com/licensing/) doc or email yo@dev.to.
<br>

View file

@ -8,7 +8,7 @@ module EdgeCacheSafetyCheck
def current_user
# In production, current_user will cause a cache leak if it's placed within an edge-cached code path.
# More information here:
# https://docs.forem.com/technical-overview/architecture/#we-cache-many-content-pages-on-the-edge
# https://developers.forem.com/technical-overview/architecture/#we-cache-many-content-pages-on-the-edge
return super unless RequestStore.store[:edge_caching_in_place]
return if session_current_user_id.blank?

View file

@ -8,7 +8,7 @@ import { Meta } from '@storybook/addon-docs/blocks';
Forem is a Rails application. Most of what we build for views uses ERB templates
(\*.html.erb files). We also build out parts of or sometimes complete views
using [Preact](https://docs.forem.com/frontend/preact), typically for the logged
using [Preact](https://developers.forem.com/frontend/preact), typically for the logged
on user experience. For example, the main page feed.
Because of that, we components that are written in pure HTML & CSS as well as

View file

@ -7,7 +7,7 @@
<div class="crayons-card crayons-card--content-rows">
<header>
<h2 class="mb-2"><%= community_name %> API Keys</h2>
<p>You can generate personal API keys to use for authentication with the <%= community_name %> API. The API is still in its beta stage. The <a href="https://docs.forem.com/api">documentation</a> contains further information.</a></p>
<p>You can generate personal API keys to use for authentication with the <%= community_name %> API. The API is still in its beta stage. The <a href="https://developers.forem.com/api">documentation</a> contains further information.</a></p>
</header>
<div>

View file

@ -296,7 +296,7 @@ Rails.application.routes.draw do
# You can have the root of your site routed with "root
get "/robots.:format", to: "pages#robots"
get "/api", to: redirect("https://docs.forem.com/api")
get "/api", to: redirect("https://developers.forem.com/api")
get "/privacy", to: "pages#privacy"
get "/terms", to: "pages#terms"
get "/contact", to: "pages#contact"

View file

@ -1,32 +0,0 @@
{
"name": "Forem Docs",
"root": "./",
"logo": "seedling.png",
"host": "0.0.0.0",
"header_links": [
{
"title": "Repo",
"href": "https://github.com/forem/forem",
"target": "_blank",
"rel": "noopener noreferrer nofollow"
},
{
"title": "DEV",
"href": "https://dev.to",
"target": "_blank",
"rel": "noopener noreferrer nofollow"
},
{
"title": "Ruby doc",
"href": "https://docs.forem.com/ruby-doc",
"target": "_blank",
"rel": "noopener noreferrer nofollow"
},
{
"title": "API doc",
"href": "https://docs.forem.com/api",
"target": "_blank",
"rel": "noopener noreferrer nofollow"
}
]
}

View file

@ -1 +0,0 @@
lts/erbium

View file

@ -1 +0,0 @@
2.7.2

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

View file

@ -1 +0,0 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 293 180"><path d="M81 1782 c-19 -9 -44 -30 -55 -45 -21 -28 -21 -35 -21 -837 0 -802 0 -809 21 -837 11 -15 36 -36 55 -45 32 -17 118 -18 1384 -18 1266 0 1352 1 1384 18 19 9 44 30 55 45 21 28 21 35 21 837 0 802 0 809 -21 837 -11 15 -36 36 -55 45 -32 17 -118 18 -1384 18 -1266 0 -1352 -1 -1384 -18z m814 -409 c50 -26 118 -98 142 -150 16 -34 18 -71 18 -348 0 -294 -1 -312 -21 -356 -31 -66 -78 -114 -142 -146 -56 -27 -61 -28 -259 -31 l-203 -4 0 532 0 531 213 -3 c195 -3 215 -5 252 -25z m795 -83 l0 -110 -165 0 -165 0 0 -100 0 -100 100 0 100 0 0 -110 0 -110 -100 0 -100 0 0 -100 0 -100 165 0 165 0 0 -110 0 -110 -225 0 c-248 0 -260 2 -309 60 l-26 32 0 434 c0 480 -1 476 63 514 29 18 52 20 265 20 l232 0 0 -110z m381 -194 c43 -164 80 -303 81 -308 2 -5 39 129 83 297 43 169 84 309 90 311 6 3 63 3 126 2 l116 -3 -123 -462 c-67 -253 -128 -472 -134 -485 -47 -90 -145 -132 -219 -94 -35 19 -83 73 -99 112 -6 16 -64 227 -128 468 -64 242 -118 446 -121 453 -4 11 18 13 123 11 l128 -3 77 -299z" transform="matrix(.1 0 0 -.1 0 180)"/><path d="M660 870 l0 -310 45 0 c52 0 99 21 115 49 6 12 10 116 10 266 l0 247 -29 29 c-26 25 -37 29 -85 29 l-56 0 0 -310z" transform="matrix(.1 0 0 -.1 0 180)"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

View file

@ -1,11 +0,0 @@
ruby_version = File.read(
File.join(File.dirname(File.dirname(__FILE__)), ".ruby-version"),
).strip
source "https://rubygems.org"
ruby ruby_version
gem "activerecord", "~> 5.2.3" # Databases on Rails
gem "yard", "~> 0.9.19" # YARD is a documentation generation tool for the Ruby programming language
gem "yard-activerecord", "~> 0.0.16" # YARD extension that handles and interprets methods used when developing applications with ActiveRecord
gem "yard-activesupport-concern", "~> 0.0.1" # YARD extension that brings support for modules making use of ActiveSupport::Concern

View file

@ -1,18 +0,0 @@
.PHONY: build
build: api-doc ruby-doc gitdocs
api-doc:
# docs/.static/api will let gitdocs serve the API doc as static content
cd .. && \
npm install -g redoc-cli@latest && \
redoc-cli bundle docs/api_v0.yml --options.suppressWarnings --options.pathInMiddlePanel --options.jsonSampleExpandLevel=all --options.menuToggle -t docs/api_template.hbs && \
mkdir -p docs/.static/api/ && \
mv redoc-static.html docs/.static/api/index.html
ruby-doc:
# docs/.static/ruby-doc will let gitdocs serve the Ruby doc as static content
cd .. && yard doc -o docs/.static/ruby-doc
gitdocs:
npm install -g gitdocs@latest && gitdocs build

View file

@ -1,24 +0,0 @@
---
title: Admin Search
---
# Admin Search
For admin views that need to take advantage of searching and filtering, we've
chosen to use [Ransack][ransack].
Ransack is a Ruby gem that makes searching relatively painless. It has excellent
documentation, but if you're looking for an example of how it's being used on
Forem, we've implemented it to help searching and sorting user reports.
The view responsible for managing user reports can be found at
`/admin/moderation/reports` and Ransack can be seen in use on the
index action of the [`admin/feedback_messages_controller`][feedback_messages].
For Forem, Ransack is being used exclusively in admin, for search problems in
other parts of the app we use [PostgreSQL Full Text Search][postgres_fts].
[feedback_messages]:
https://github.com/forem/forem/blob/4e41e4a2ac893fa2a6c36990cfe475858ffb086a/app/controllers/admin/feedback_messages_controller.rb#L4
[ransack]: https://github.com/activerecord-hackery/ransack
[postgres_fts]: https://www.postgresql.org/docs/11/textsearch.html

View file

@ -1,43 +0,0 @@
---
title: Admin User Interface
---
# User Interface
Our admin dashboard is primarily ERB views that render on the server. Largely,
we try to adhere to
[ActionView](https://guides.rubyonrails.org/action_view_overview.html)'s
conventions in these views.
For layout, basic styles, and some interactions, we use
[Bootstrap 4](https://getbootstrap.com/). Forem isn't dedicated to using
Bootstrap for everything, but because our design team hasn't spent much time on
these views, we find it's easier to stick with something many developers already
know.
When a view requires some custom interactivity, we've historically leaned on
vanilla JavaScript or jQuery, but going forward we've elected to use
[StimulusJS](https://stimulusjs.org/) for DOM manipulation and interactivity
inside of admin.
# Forms
Inside of the admin views, we're
[actively moving from the old ERB syntax for forms](https://m.patrikonrails.com/rails-5-1s-form-with-vs-old-form-helpers-3a5f72a8c78a).
We tend to prefer the `form_with` helper over the previous `form_for` and
`form_tag` helpers.
# StimulusJS
Stimulus is a modest frontend framework; its primary purpose is manipulating
HTML. It does not provide templating features.
In the Forem application, [Webpacker](/frontend/webpacker/) is used to load
Stimulus controllers via the `app/javascript/packs/admin.js`
[pack file](https://github.com/rails/webpacker/blob/main/docs/folder-structure.md#packs-aka-webpack-entries).
Ideally, controllers serve as an abstraction for shared functionality between
views.
New controllers can be added in `/app/javascript/admin/controllers`. Unit tests
should exist for each controller in the adjacent
`/app/javascript/admin/__tests__/contollers` directory.

View file

@ -1,48 +0,0 @@
---
title: Admin Guide
items:
- admin-search.md
- internal-user-interface.md
---
# Admin Guide
The Forem application contains a rudimentary administration dashboard that lives
behind the admin route.
The admin dashboard is made up of a series of views that range from
administration tools to simplified reports. These tools are used by users with
the `admin` or `super_admin` roles to administrate the Forem application.
Authorization for these tools is handled by the [Rolify][rolify] gem.
Currently, a workaround exists to give access to certain `admin` views via
Rolify by assigning the role `single_resource_admin` to a user.
`single_resource_admin` users are given access to a Ruby class. In the codebase,
there are admin models, not backed by database tables, that exist for this
purpose. For example, if you needed to give a user access to only
`/admin/apps/welcome`, you'd run the following command in the Rails console:
```ruby
user = User.find(some_user_id)
user.add_role(:single_resource_admin, Welcome)
```
This gives the user administration privileges on the controller associated with
an almost empty Rails model that lives in `app/models/admin/welcome.rb`:
```ruby
class Welcome < ApplicationRecord
resourcify
# This class exists to take advantage of Rolify for limiting authorization
# on admin reports.
# NOTE: It is not backed by a database table and should not be expected to
# function like a traditional Rails model
end
```
Now that user will be able to access the view at `admin/welcome`. The same
workaround has been implemented for most of the admin views.
[rolify]: https://github.com/RolifyCommunity/rolify

View file

@ -1,33 +0,0 @@
{{! This template is used by ReDoc to render the final API doc }}
{{! It is written in HandlerbarsJS syntax }}
{{! Please refer to https://github.com/Redocly/redoc/blob/master/cli/README.md }}
{{! and https://github.com/Redocly/redoc/blob/master/cli/template.hbs }}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8" />
<title>DEV API (beta)</title>
<meta name="description" content="DEV REST API documentation">
<meta property="og:type" content="article" />
<meta property="og:url" content="https://docs.forem.com/api" />
<meta property="og:title" content="DEV API (beta)" />
{{! needed for adaptive design }}
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
padding: 0;
margin: 0;
}
</style>
{{{redocHead}}}
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
</head>
<body>
{{{redocHTML}}}
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -1,65 +0,0 @@
---
title: Audit Log
---
# Audit Log
To help maintain accountability for users with elevated permissions the Forem
application has a special model that records certain actions.
For example, when a user with the `trusted` role creates a negative reaction on
an article a record is created with certain information about that action.
That record (which we call an `AuditLog`) looks something like this:
```ruby
#<AuditLog:0x00005629f019a490
id: 1,
category: "moderator.audit.log",
created_at: Thu, 07 May 2020 20:25:31 UTC +00:00,
data:
{"action"=>"create", "category"=>"vomit", "controller"=>"reactions", "reactable_id"=>"16", "reactable_type"=>"Article"},
roles: ["trusted"],
slug: "create",
updated_at: Thu, 07 May 2020 20:25:31 UTC +00:00,
user_id: 21>
```
You can see from this record that the user with an id of `21` created a vomit
reaction on the article with an id of `16`. If that's not obvious to you from
this object, don't worry, just take our word on it.
You can find an example of `Audit::Logger` in action in
`app/controllers/admin/reactions_controller.rb`:
```ruby
after_action only: [:update] do
Audit::Logger.log(:moderator, current_user, params.dup)
end
```
This code creates a record to indicate that a someone modified a reaction from
the admin controller.
It's a good idea to add a similar `after_action` to any controller action that
might benefit from increased transparency.
Additionally, the `AuditLog` is used to track important actions performed on a
user's account, e.g. adding or removing a credit card:
```ruby
#<AuditLog:0x00000001193ce348> {
category: "user.credit_card.edit",
created_at: Tue, 21 Jul 2020 06:35:13 +03 +03:00,
data: {
"action" => "create",
"controller" => "stripe_active_cards",
"user_action" => "add"
},
id: 4,
roles: [],
slug: "credit_card_add",
updated_at: Tue, 21 Jul 2020 06:35:13 +03 +03:00,
user_id: 53
}
```

View file

@ -1,75 +0,0 @@
---
title: Apple Authentication
---
# Sign in with Apple Authentication
Forem allows you to authenticate using
[Sign in with Apple](https://developer.apple.com/sign-in-with-apple/). In order
to use this authentication method you'll need to be enrolled to the
[Apple Developer Program](https://developer.apple.com/programs/) in order to
retrieve the necessary credentials and an HTTPS supported URL for the callback
configuration (HTTP won't work). Then you'll need to provide the keys to the
Rails application.
# Apple Developer Portal Configuration
[Register/Sign in](https://developer.apple.com/account) to your Apple Developer
Account.
## Service ID Configuration
1. [Create a Service ID](https://developer.apple.com/account/resources/identifiers/list/serviceId)
![Create Service ID](https://user-images.githubusercontent.com/6045239/92610177-a5cc9e00-f274-11ea-9f63-20d8356d0bee.png)
2. Name the Service and finalize the registration
![Naming Service ID](https://user-images.githubusercontent.com/6045239/92610168-a36a4400-f274-11ea-8f79-7516c0c6c9c3.png)
3. Configure Domains and Subdomains & the callback URL. This example uses
[ngrok](https://ngrok.io) for HTTPS support.
![Callback URLS](https://user-images.githubusercontent.com/6045239/92610184-a8c78e80-f274-11ea-9439-a98c6b627567.png)
## Key Configuration
1. [Register a new Key](https://developer.apple.com/account/resources/authkeys/add).
Enable the "Sign in with Apple" option and configure it so it's associated
with the corresponding App ID
![Register a new Key](https://user-images.githubusercontent.com/6045239/92611125-b3ceee80-f275-11ea-9c00-e1b5ca2f9af0.png)
2. Download the Key
![Download the Key](https://user-images.githubusercontent.com/6045239/92611466-0f00e100-f276-11ea-912d-f8a74b6dfb04.png)
# Configuring the Rails Application
Now with both the Service ID and Key you'll need to enable Apple Authentication
and pass in the credentials in the admin dashboard
`/admin/customization/config`.
![Admin Authentication Configuration Dashboard](https://user-images.githubusercontent.com/6045239/92613383-25a83780-f278-11ea-94a7-b710da544c9d.png)
Add the corresponding configuration data. Make sure the PEM key you downloaded
has explicit linebreaks (`\n`), don't forget the one at the very end of it.
![Apple config](https://user-images.githubusercontent.com/6045239/92614087-e0d0d080-f278-11ea-8d20-45148e1a6b59.png)
Save the changes and restart your server for these values to take effect.
## Email configuration
Apple uses what they call Private Email Relay Service to hide user's emails. For
this to work first
[create a new email source](https://developer.apple.com/account/resources/services/list).
![Email configuration](https://user-images.githubusercontent.com/6045239/92612469-22607c00-f277-11ea-918d-697cf4a18b15.png)
Emails sent need to be authenticated and the configuration depends on the
different providers available:
- [Mailchimp](https://mailchimp.com/help/set-up-custom-domain-authentication-dkim-and-spf/)
- [SendGrid](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/)
- [SES](https://docs.aws.amazon.com/es_es/ses/latest/DeveloperGuide/send-email-authentication-dkim.html)

View file

@ -1,69 +0,0 @@
---
title: Facebook Authentication
---
# Facebook App and Authentication
Forem allows you to authenticate using Facebook. In order to use this
authentication method in local development, you will need to setup a Facebook
App and retrieve its keys. Then you'll need to provide these keys to the Rails
application.
## Sign up
1. [Sign in](https://facebook.com) to your Facebook account.
2. In order to get the API keys, you will have to
[convert your account to a developer account](https://developers.facebook.com/).
## Get API keys
1. [Sign up](#facebook-sign-up) or [sign in](https://developers.facebook.com) to
your Facebook developer account.
2. From **My Apps** dashboard, click on **Add a New App**.
![facebook-1](https://user-images.githubusercontent.com/37842/90912963-1f254f00-e3a1-11ea-9db9-2b77bddfe185.png)
3. Select **For Everything Else**
![facebook-2](https://user-images.githubusercontent.com/37842/90913109-627fbd80-e3a1-11ea-8d78-d0b2bde76b3d.png)
4. Fill in the app display name and contact email, then click on **Create App
ID**
![facebook-3](https://user-images.githubusercontent.com/37842/90913171-7b886e80-e3a1-11ea-9359-c4642c05c7b6.png)
5. On the **Add a Product** screen, click **Set Up** under the **Facebook
Login** section
![facebook-4](https://user-images.githubusercontent.com/37842/90913219-8d6a1180-e3a1-11ea-86cb-d0b0d8681887.png)
6. Ignore the quickstart options, and click **Settings -> Basic** in the sidebar
![facebook-5](https://user-images.githubusercontent.com/37842/90913319-b5f20b80-e3a1-11ea-866a-0b06cf3296c7.png)
7. From the basic settings screen dashboard copy the **App ID** and **App
Secret** values to your `.env` file accordingly (name of Facebook key -> name
of our `Settings::General` variable).
```text
APP ID -> FACEBOOK_APP_ID
API secret -> FACEBOOK_APP_SECRET
```
![facebook-6](https://user-images.githubusercontent.com/37842/90913396-d5893400-e3a1-11ea-93f5-a0fbb06a0c53.png)
## Configure the Facebook App
1. From the basic settings screen dashboard set your application's domain name
in **App Domains** field, and be sure to click **Save Changes**
![facebook-7](https://user-images.githubusercontent.com/37842/91494838-20fa8100-e87f-11ea-8609-0d3b92edc093.png)
2. Naviate to **Facebook Login --> Settings**, and enter the following callback
URL in the field **Valid OAuth Redirect URIs**:
`https://<your domain>>/users/auth/facebook/callback`
![facebook-8](https://user-images.githubusercontent.com/37842/91509964-48624580-e8a1-11ea-9f7b-931cfcb7cd87.png)

View file

@ -1,31 +0,0 @@
---
title: GitHub Authentication
---
# GitHub App and Authentication
Forem allows you to authenticate using GitHub. To use this authentication method
in local development, you will need to set up a GitHub App and retrieve its
keys. Then you'll need to provide these keys to the Rails application.
1. [Click this link to create a new OAuth application in your Github account](https://github.com/settings/applications/new) -
you will be redirected to sign in to Github account if you have not already.
2. Fill in the form with an application name, description, and the URL
`http://localhost:3000/users/auth/github/callback`. Replace the port `3000` if you run Forem on another
port.
![github-1](https://user-images.githubusercontent.com/8124558/107048692-37dec100-6797-11eb-83d0-1e72db033522.png)
3. You will be redirected to the app's **Developer settings**. Here you will
find the keys. Add them to your `.env` file accordingly (name of GitHub key
-> name of our `ENV` variable):
```text
Client ID -> GITHUB_KEY
Client Secret -> GITHUB_SECRET
```
![github-2](https://user-images.githubusercontent.com/22895284/51085862-49337b80-173f-11e9-8503-f8251d07f458.png)
4. Done.

View file

@ -1,97 +0,0 @@
---
title: Twitter Authentication
---
# Twitter App and Authentication
Forem allows you to authenticate using Twitter. In order to use this
authentication method in local development, you will need to setup a Twitter App
and retrieve its keys. Then you'll need to provide these keys to the Rails
application.
## Sign up
1. [Sign in](https://developer.twitter.com/apps) to your Twitter account.
2. In order to get the API keys, you will have to
[apply for a developer account](https://developer.twitter.com/en/apply-for-access).
Click the **Apply** button.
![twitter-up-1](https://user-images.githubusercontent.com/22895284/51078779-53139b00-16bb-11e9-911c-f232e229872a.png)
3. Setup your Twitter account. Be sure you have your phone number and email
address filled in.
![twitter-up-2](https://user-images.githubusercontent.com/22895284/51078780-53139b00-16bb-11e9-91d5-08c9365ff08f.png)
4. Fill in your account information and give a name to your **developer
account**.
![twitter-up-3](https://user-images.githubusercontent.com/22895284/51078781-53ac3180-16bb-11e9-8cf4-005efbb92d8a.png)
5. Write down the reasons that you want to use Twitter API. Mention Forem's
community and describe the issues and tests and things that you want to work
on. Copy it, you might use it later ;)
![twitter-up-4](https://user-images.githubusercontent.com/22895284/51078782-53ac3180-16bb-11e9-9937-c888ae40143c.png)
6. Read :) and accept the Terms and Conditions.
![twitter-up-5](https://user-images.githubusercontent.com/22895284/51078783-53ac3180-16bb-11e9-9cf1-8e009ada6e57.png)
7. Verify your email address once more, and you will be done.
8. You are done.
## Get API keys
1. [Sign up](#twitter-sign-up) or [sign in](https://developer.twitter.com/apps)
to your Twitter developer account.
2. From **Apps** dashboard, click on **Create and app**.
![twitter-1](https://user-images.githubusercontent.com/22895284/51078797-9a019080-16bb-11e9-8130-1cd13008461e.png)
3. Fill in the app name, description, and URL `https://dev.to`.
![twitter-2](https://user-images.githubusercontent.com/22895284/51078798-9a019080-16bb-11e9-900d-d2677d7c43c4.png)
4. Check the **Enable Sign in with Twitter** option and fill in the Callback URL
`http://localhost:3000/users/auth/twitter/callback` (or whatever port you run
Forem on).
![twitter-3](https://user-images.githubusercontent.com/22895284/51078799-9a9a2700-16bb-11e9-8e88-0393260449c7.png)
5. Fill in the information, **Terms of Service** `http://dev.to/terms` and
**Privacy policy** `http://dev.to/privacy`.
![twitter-4](https://user-images.githubusercontent.com/22895284/51078800-9a9a2700-16bb-11e9-9b36-d325a2624f5a.png)
6. Write down (or paste) the things that you will work on. Press **Create**.
![twitter-5](https://user-images.githubusercontent.com/22895284/51078801-9a9a2700-16bb-11e9-9bd9-76c9ca1ba526.png)
7. Review the
[Twitter Developer Terms](https://developer.twitter.com/en/developer-terms/agreement-and-policy.html)
and agree to do nothing sketchy.
![twitter-6](https://user-images.githubusercontent.com/22895284/51078802-9a9a2700-16bb-11e9-8789-53720bcfc9d9.png)
8. The app is all set!
9. One more change: From the app dashboard, go to **Permissions** and check
**Request email addresses from users** option.
![twitter-7](https://user-images.githubusercontent.com/22895284/51078803-9a9a2700-16bb-11e9-8f27-dbfe04b52031.png)
10. From the same dashboard access the **Keys and tokens** and add them to your
`.env` file accordingly (name of Twitter key -> name of our `ENV` variable).
Be sure to copy the _access token_ and _access token secret_ right away
because it will be hidden from you in the future.
```text
API key -> TWITTER_KEY
API secret key -> TWITTER_SECRET
```
![twitter-8](https://user-images.githubusercontent.com/47985/72329507-72d30a00-36e7-11ea-83ac-ebea5d41ba39.png)

View file

@ -1,20 +0,0 @@
---
title: Authentication
---
# Authentication
Authentication is handled by [Devise](https://github.com/plataformatec/devise)
and [OmniAuth](https://github.com/omniauth/omniauth).
On Forem you can authenticate through Facebook, GitHub, or Twitter. Please check out
the respective guides on how to authenticate:
- [Apple authentication](/backend/auth-apple)
- [Facebook authentication](/backend/auth-facebook)
- [Github authentication](/backend/auth-github)
- [Twitter authentication](/backend/auth-twitter)
We may add other authentication providers in the future. Please check back
again, or search
[our GitHub repository's issues.](https://github.com/forem/forem/issues)

View file

@ -1,15 +0,0 @@
---
title: Authorization
---
# Authorization
Authorization is handled by the third-party gem
[Pundit](https://github.com/varvet/pundit) through the `authorize` method which
you can find in various controllers, look for statements like:
```ruby
authorize @user
```
All authorization policies can be found in `/app/policies`.

View file

@ -1,81 +0,0 @@
---
title: Configuration
---
# Configuration
We currently use the following gems for configuring the application:
- [dotenv](https://github.com/bkeepers/dotenv)
- [rails-settings-cached](https://github.com/huacnlee/rails-settings-cached)
- [vault](https://github.com/hashicorp/vault-ruby)
## dotenv
This gem is used for configuring environment variables for test and development
environments. Examples:
- `REDIS_URL`
- `FASTLY_API_KEY`
- `STRIPE_SECRET_KEY`
Settings managed via your ENV can be found in
[Configuring Environment Variables](/getting-started/config-env)) and viewed at
`/admin/customization/config` (see [the Admin guide](/admin)):
![Screenshot of env variable admin interface](https://user-images.githubusercontent.com/47985/73627243-67d41f80-467e-11ea-9121-221275ff8a89.png)
## rails-settings-cached
We use this gem for managing settings used within the app's business logic.
Examples:
- `Settings::General.main_social_image`
- `Settings::RateLimit.follow_count_daily`
- `Settings::Authentication.twitter_secret`
These settings can be accessed via the
[`Settings::General`](https://github.com/forem/forem/blob/master/app/models/settins/general.rb)
object and various models in the `Settings::` namespace and viewed / modified
via `/admin/customization/config` (see [the Admin guide](/admin)).
![Screenshot of settings admin interface](https://user-images.githubusercontent.com/47985/73627238-6276d500-467e-11ea-8724-afb703f056bc.png)
## Vault
The vault Ruby gem allows us to interact with
[Vault](https://www.vaultproject.io/docs/what-is-vault). In a nutshell, Vault is
a tool for securely storing and accessing secrets. It is completely optional for
running a Forem. To access it we use the wrapper `AppSecrets`.
```ruby
class AppSecrets
def self.[](key)
result = Vault.kv(namespace).read(key)&.data&.fetch(:value) if ENV["VAULT_TOKEN"].present?
result ||= ApplicationConfig[key]
result
rescue Vault::VaultError
ApplicationConfig[key]
end
def self.[]=(key, value)
Vault.kv(namespace).write(key, value: value)
end
def self.namespace
ENV["VAULT_SECRET_NAMESPACE"]
end
private_class_method :namespace
end
```
We attempt to access a secret from Vault if it is enabled, i.e. if the
`VAULT_TOKEN` is present. If Vault is not enabled or if we cannot find the
secret in it, then we fallback to fetching the secret from the
`ApplicationConfig`.
One advantage of using Vault with Forem is that it allows you to update your
secrets easily through the application rather than having to mess with ENV
files. If you would like to try out Vault, follow our
[installation guide for setting it up locally](/installation/vault).

View file

@ -1,101 +0,0 @@
---
title: Data Update Scripts
---
## What are Data Update Scripts?
Data Update Scripts were introduced in
[this PR](https://github.com/forem/forem/pull/6025) and allow us to run any data
updates we might need. For example, if we added a column to the database and
then wanted to backfill that column with data, rather than going and manually
doing it in a console, we would use a DataUpdateScript.
## How it works
First off, we added a
[DataUpdateScript model](https://github.com/forem/forem/blob/master/app/models/data_update_script.rb)
to Rails and a corresponding database table. This table is what we use to keep
track of what scripts have been run and which ones have not/still need to be.
To create a script you can use our custom Rails generator:
```
rails generate data_update BackfillColumnForArticles
```
This will create a simple Ruby class like below and all you have to do is fill
in the code it will run.
```ruby
module DataUpdateScripts
class BackfillColumnForArticles
def run
# Place your data update logic here
# Make sure your code is idempotent and can be run safely
# multiple times at any time
end
end
end
```
The generator will also automatically create the corresponding spec file.
```ruby
require "rails_helper"
require Rails.root.join(
"lib/data_updates/20201103042915_backfill_column_for_articles.rb",
)
describe DataUpdateScripts::BackfillColumnForArticles do
pending "add some examples to (or delete) #{__FILE__}"
end
```
While we encourage adding tests for data update scripts, you can skip spec
creation by adding the `--no-spec` option to the `rails generate` command:
```
rails generate data_update BackfillColumnForArticles --no-spec
```
Once your script is in place then you can either run `rails data_updates:run`
manually or you can let our setup script handle it. In our local
[bin/setup](https://github.com/forem/forem/blob/main/bin/setup) script you will
see we have added an additional task to update data. This kicks off the rake
task `data_updates:run` for you.
The rake task itself will check the `lib/data_update_scripts` folder to see if
there are any new scripts that need to be run. It does this by reading all of
the files and then checking to see if they have a corresponding database entry.
If they do not, then we create a new one and run the script. If a database entry
already exists and it indicates the script has been run, then we skip that
script.
## In production
DataUpdateScripts are also run automatically when a production deploy goes out.
However, to ensure the new code they need to use has been deployed we use a
[`DataUpdateWorker`](https://github.com/forem/forem/blob/main/app/workers/data_update_worker.rb)
via Sidekiq and set it to run 10 minutes after the deploy script has completed.
## Best practices
### Working with large collections of rows
From time to time, scripts need to operate on a large amount of rows; in those
cases we encourage:
- adding explicit logging to the script
- reversing the order, to start processing the most recent records first
For example:
```ruby
def run
Article.find_each(order: :desc).with_index do |article, index|
Rails.logging.info("...") if index % 1000 == 0 # this will log every 1000 articles
article.save
end
end
```

View file

@ -1,34 +0,0 @@
---
title: Emails
---
# Setting up email
If you would like to enable transactional email using services like SendGrid or
Mailgun, you can configure it by using the following environment variables:
```shell
SMTP_ADDRESS= # ie. "smtp.sendgrid.net"
SMTP_PORT= # ie. 587
SMTP_DOMAIN=
SMTP_USER_NAME=
SMTP_PASSWORD=
SMTP_AUTHENTICATION= # defaults to :plain
```
We follow the standard `ActionMailer` configuration. For more info, please check
out Rails'
[official documentation](https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration).
# Previewing emails in development
You can view email previews at <http://localhost:3000/rails/mailers>.
Previews are setup in the directory `spec/mailers/previews`.
# Overriding mailer templates
To update the contents of emails that the app sends, edit the views under
`app/views/mailers`. Note that this app uses the
[`devise_invitable` gem](https://github.com/scambra/devise_invitable) for
invitations. The views for this gem are stored under `app/views/devise/mailer`.

View file

@ -1,63 +0,0 @@
---
title: Fastly
---
## What is Fastly?
[Fastly](https://www.fastly.com/) is a third party service we use for caching on
the edge. It allows us to scale up and serve our millions of visitors quickly
and efficiently.
If you want to learn more about we use Fastly, check out this
[talk](https://www.youtube.com/watch?v=Afy7H04X9Us) that one of our founders,
[@benhalpern](https://dev.to/ben), gave at RailsConf 2018 talking about how we
made our app so fast it went viral.
Fastly offers many different configurations (conditions,
[snippets](https://docs.fastly.com/vcl/vcl-snippets/about-vcl-snippets/), etc.).
## Snippets
Snippets, in general, are _"short blocks of VCL logic that can be included
directly in your service configurations. They're ideal for adding small sections
of code when you don't need more complex, specialized configurations that
sometimes require custom VCL."_
You can learn more about VCL snippets on
[Fastly's website](https://docs.fastly.com/vcl/vcl-snippets/about-vcl-snippets/)
### Adding query string parameters to a safe list
In the context of contributing, here's what you need to know about Fastly. In
order for our servers to receive any sort of query string parameters in a
request, they must first be marked as safe in Fastly. For example, if you're
creating a new API endpoint or updating an existing one to accept new
parameters, you'll need to update Fastly.
The reason we have a safe list of parameters in Fastly this way is so we don't
have to consider junk parameters when busting the caches. Check out our
[`EdgeCache` services](https://github.com/forem/forem/tree/main/app/services/edge_cache)
to see examples of this.
Previously this was a manual process done by an internal team member. Now we do
it programmatically using the Fastly
[gem](https://github.com/fastly/fastly-ruby).
### How it works
We created a new file, `config/fastly/safe_params_list.vcl`, to house all of the
safe params in Fastly.
If you need to update this list, simply update this file. It's as easy as that!
This is a VCL file and you'll see a little Regex checking for a list of safe
params.
_Fastly is not setup for development._
### In production
If you have Fastly configured in Production, all Fastly configs are updated
automatically when a production deploy goes out.
We do this by executing `bin/rails fastly:update_configs` in our
`release-tasks.sh` script.

View file

@ -1,138 +0,0 @@
---
title: Internationalization (i18n)
---
# Internationalization (i18n)
## What is internationlization (i18n)?
To over simplify the concept a bit, internalization (i18n for short) is the
process of making the platform more user-friendly in various languages for
people around the globe. This includes, but is certainly not limited to, things
like making the site available in different languages, changing currency values
to match your region, changing date formats, etc.
## What do we currently support?
We introduced some routing to lay the groundwork for a more comprehensive i18n
implementation.
## What is the goal?
We want everyone to feel included, regardless of where they're located or what
language(s) they speak. The goal is to make the platform available in various
languages.
## How do you get involved?
The following is a high level outline of an approach to internationalization.
This is by no means set in stone.
We encourage you to open a pull request (PR) to this documentation or to
contribute to internationalization with your ideas - we're
[open-source](https://github.com/forem/forem/pulls)!
## Routing
We have logic for routes setup. You can visit a page and add `/locale/:locale`
to the beginning of the path. For example, if you visit the homepage, you can
add `/locale/fr-ca` for French, Canadian where `fr` is the language code and
`ca` is the region code.
Setting up languages under this "sub-folder" approach helps with Search Engine
Optimization (SEO), routing, and more.
_Currently, the various language routes will not do anything - it will stil show
the site in English (US)._
Once i18n is up and running, users will be able to select their preferred
language to view the platform in. These routes will be the location of various
languages.
## Translating content
There are many ways to translate static content on the platform. To start, we
can explore tools like [i18n-tasks](https://glebm.github.io/i18n-tasks/) which
also has an option to leverage Google Translate programmatically. We'll need to
create locale files (likely `.yml`) to house the translations.
## Search Engine Optimization (SEO)
It seems search engines, especially Google, don't particularly like content on a
page to be in multiple languages. To account for this on pages like articles, we
can try an approach using the canonical URL for the language the article was
written in.
For example, if we detect an article is written in Spanish, we can set the
canonical URL for that article to be `/locale/es/username/article-slug`. We can
then hide comments that are not in the same language as the article/rest of the
page (Spanish in this example) only for the views the crawlers would see. That
way, when the search engine crawler hits an article written in Spanish, the
crawler will see the entire page in Spanish. We will not hide comments for the
views that real users see.
## Caching
The platform relies on edge caching, especially with regards to articles. To
account for this, we'll need to add logic at the edge that understands what
languages the platform currently supports and where to look up the language
variant in the cache.
If the edge doesn't pick up on a user selected preference (possibly sent as an
additional header or cookie), the edge will look at the `Accept-Language` header
and normalize it. The header can include more specific preferences and look
something like: `Accept-Language: fr-ca, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5`.
There are 2 things going on here. 1) A user can specify country/region variants
for a language - `fr-ca` (French - Canada) and `fr-fr` (French - France). For
simplicity's sake, we want to normalize that sort of preference to `fr` to
start. 2) A user can specify priority using the q argument. We'll want to
interpret the user's priority preferences to match their highest priority
language with one we currently support.
Once the edge is aware of what language it should be looking for, it will set
the cache key accordingly.
We also make use of fragment caching in several places. We need to update the
keys for those caches to account for `locale` so we're not mistakenly serving a
cached fragment in a different language than intended.
## Additional considerations
- _Translating URLs_. For the best SEO result, we should also translate URLs
themselves into various languages. Something like the `/about` page could be
translated, for example. For now, we aren't going to account for this.
- _Translating dynamic/user generated content_. For now, we plan to _not_
automatically translate any dynamic/user generated content (articles,
comments, listings, etc.). In the future we could explore what that looks
like, how a user can opt-in/out of that, etc.
## Styles, design, and UI
We'll want to expand some design aspects to support other languages that may be
right-to-left, have different spacings, have special characters, etc.
## Next steps
A few next steps we can take on the road to internationalization.
- Update our logic to allow special characters/encodings in URLs. Currently, we
generate slugs on dynamic content like articles and tags that may include
characters that make the URL
invalid.[Here](https://github.com/forem/forem/issues/10116) is a good example.
We want to update this logic so these characters work in URLs as expected.
- Allow Forem Admins to set a "default language". Currently, if a user doesn't
select a language preference, it defaults to English ("en").
- Clean up some code. There are some places we're hard-coding strings on the
frontend. We'll want to explore moving that sort data to the backend to unify
where and how we're translating.
- Translate areas of the site into English (US) first to ensure things are still
working. In other words, have the platform adhere to the default locale
instead of hard-coded strings.
- Start translating!
## Resources
- [Rails Guides - Rails Internationalization (I18n)](https://guides.rubyonrails.org/i18n.html)
- [i18n-tasks](https://glebm.github.io/i18n-tasks/)
- [Google: Managing multi-regional and multilingual sites](https://support.google.com/webmasters/answer/182192)
- [forem.dev post: What internationalization features should we support?](https://forem.dev/vaidehijoshi/what-internationalization-features-should-we-support-4kl)

View file

@ -1,33 +0,0 @@
---
title: Metrics
---
## Time series data
We track point-in-time data by sending data points to Datadog using
[Statdsd](https://github.com/DataDog/dogstatsd-ruby).
Currently, we run a daily metric fetching job in `fetch.rake` which is called
via the Heroku scheduler. If we wanted to do other frequencies like hourly, etc.
we could add similar tasks.
The rake task calls `Metrics::RecordDailyUsageWorker.perform_async`, which
performs algorithms to come up with data points to send, for example:
```ruby
DataDogStatsClient.count("users.active_days_past_week", one_day_users, tags: { resource: "users", group: "new_users, day_count: 1 })
```
If you want to create a new periodic data send, follow this pattern to do so.
## Vendor-Agnostic
While we currently are not vendor-agnostic in how we do this (Heroku/Datadog),
it is set up in a way that could become so in the future. The main pattern is
`Every x minutes/hours/etc. send aggregate data to warehouse where it can be examined on a timeseries basis`.
This could, in the future, be bundled right into the platform using an open
source timeseries database and data visualization.
Once in Datadog, dashboards can created using
![Datadog metrics](https://dev-to-uploads.s3.amazonaws.com/i/98rju6kzxeosf6m0jfhy.png)

View file

@ -1,30 +0,0 @@
---
title: Notifications
---
# Notifications
Since notifications are run asynchronously, we'll want to make sure jobs are
running: `bundle exec sidekiq`. If that's not running, you won't receive any
notifications. You might need to create another account to pass notifications
back and forth if you're doing this all through the UI.
Otherwise, you can generate notifications from the Rails console and run the
class methods from `app/models/notification.rb`. For example:
```ruby
# follow notification
me = User.last
follow = User.first.follow(me)
Notification.send_new_follower_notification_without_delay(follow)
# reaction notification
rxn = Reaction.create(
user_id: 1,
category: "like",
reactable: me.articles.last, # this assumes you have an article written
)
Notification.send_reaction_notification_without_delay(rxn, me)
```
Notice you have to run these methods `without_delay` since this is assuming jobs
are not running.

View file

@ -1,26 +0,0 @@
---
title: Push Notifications
---
# Push Notifications Delivery
Forem instances rely on [Rpush](https://github.com/rpush/rpush) to deliver push
notifications. This decision was heavily influenced by the desire to provide as
much flexibility as possible to Creators. In order to do this, Forem instances
can register and configure a `ConsumerApp`.
These consumer apps represent mobile applications that users use to browse and
consume content on a Forem. Authenticated users of a specific Forem instance can
register a `Device` associated to a `ConsumerApp`. With these pieces we are able
to deliver push notifications to users devices.
The `ConsumerApp` configuration page can be found at
`/admin/apps/consumer_apps`. The official Forem apps are supported by default
and require their secret credential to be provided via ENV variable.
## Rpush Implementation
We use Rpush's `rpush-redis` implementation (read
[this thread](https://github.com/forem/forem/pull/12419/files#r564660917) for
the reasons why), hence all `Rpush` models are persisted in Redis. More details
about how this works [here](https://github.com/rpush/rpush/wiki/Using-Redis).

View file

@ -1,42 +0,0 @@
---
title: Pusher
---
# Pusher for Realtime Notifications
Pusher is a third party service being used to power the
[chat system](https://dev.to/connect) and Push Notifications on
[iOS](https://apps.apple.com/us/app/dev-community/id1439094790) &
[Android](https://play.google.com/store/apps/details?id=to.dev.dev_android)
native apps.
## Chat System
In order to use the chat functionality within your development environment, you
will need to sign up for a free-tier Pusher account and retrieve its keys. Then
you'll need to provide those keys to the Rails application.
1. [Sign up](https://dashboard.pusher.com/accounts/sign_up) or
[sign in](https://dashboard.pusher.com/) to your Pusher account.
2. Once signed in, fill in the prompt to create a new Pusher Channels app.
![pusher-1](https://user-images.githubusercontent.com/22895284/51086056-058e4100-1742-11e9-8dca-de3e47e2bc73.png)
3. In your new Pusher Channels app, click the "App Keys" tab.
![pusher-2](https://user-images.githubusercontent.com/22895284/51086057-058e4100-1742-11e9-9fb7-397187aa8689.png)
4. Change your keys accordingly (name of Pusher key -> name of our application
key):
```text
app_id -> PUSHER_APP_ID
key -> PUSHER_KEY
secret -> PUSHER_SECRET
cluster -> PUSHER_CLUSTER
```
![pusher-3](https://user-images.githubusercontent.com/22895284/51086058-0626d780-1742-11e9-9c2a-26b9b10fa77f.png)
5. Done.

View file

@ -1,27 +0,0 @@
---
title: Backend Guide
items:
- audit-log.md
- authentication.md
- auth-apple.md
- auth-facebook.md
- auth-twitter.md
- auth-github.md
- authorization.md
- configuration.md
- data-update-scripts.md
- emails.md
- fastly.md
- internationalization.md
- roles.md
- pusher.md
- resource-admin.md
- notification.md
- scheduled-jobs.md
- metrics.md
- push-notifications.md
- tracking.md
- service-objects.md
---
# Backend Guide

View file

@ -1,75 +0,0 @@
---
title: Roles
---
# Roles
## What is a role?
If authorization is about who has permission to be allowed to do what you want
to do, then Roles are common patterns of authorization across users - reducing
the administrative overhead.
## Why do I need to know about roles?
Some bugs can only be seen for users with specific roles. You will need to
change the role to reproduce a problem.
## How do we implement roles in Forem?
Roles are implemented in this application using [Rolify][1]. The list of roles
can be found in [app/models/role.rb][2] and you can search for [has_role in the
codebase][3] to find which pages need which roles.
A new user starts without any roles, and there is no administrative way of
adding roles to users yet. To assign a user a role you will have to run commands
at the console.
## Example of adding permissions to a user
- open the Rails console
```shell
rails console
```
- after verifying the user `test_user_name` is missing the `trusted` role we
proceed to add it and then verify the role has been added:
```ruby
> user = User.find_by(username: "test_user_name")
> user.has_role?(:trusted)
=> false
> user.add_role(:trusted)
=> #<Role:
...
name: "trusted"
.. >
> user.has_role?(:trusted)
=> true
```
Another common requirement is changing to the administrative role, and an
example of this is found [on the admin page][5].
## Verification
A more complex query to list all the users and their roles:
```ruby
User.joins(:roles).order(:id).group(:id).pluck(:id, :username, Arel.sql("array_agg(roles.name)"))
```
## Further Reading
1. [Rolify README.md][1]
2. [What is the purpose of Rolify?][4]
3. [Admin][5]
[1]: https://github.com/RolifyCommunity/rolify
[2]: https://github.com/forem/forem/blob/main/app/models/role.rb
[3]: https://github.com/forem/forem/search?q=has_role&unscoped_q=has_role
[4]: https://stackoverflow.com/a/16096790/1511504
[5]: /backend/resource-admin

View file

@ -1,27 +0,0 @@
---
title: Scheduled Jobs
---
# Scheduled Jobs
As in the [Technical Overview](/technical-overview), We use
[Heroku Scheduler](https://devcenter.heroku.com/articles/scheduler) for
scheduled jobs. As the name suggests, this is for regularly recurring tasks that
need to be run every day, week, month, year, decade, and century.
Tasks are implemented in `forem/lib/tasks/fetch.rake`, typically in the form of:
```
task some_unique_task_name:, [optional_arg] => :environment do |optional_arg|
... do your thing here ...
end
```
You can explore the
[official Heroku documentation](https://devcenter.heroku.com/articles/scheduler#defining-tasks)
for defining tasks and read through tasks we have implemented for busting cache,
awarding badges, and more.
In your Pull Request, communicate with a Forem Core Team Member to discuss at
what frequency and ensure your task is scheduled on Heroku once your code is
reviewed, approved, and merged.

View file

@ -1,114 +0,0 @@
---
title: Service Objects
---
## What are Service Objects
Service objects are Plain Old Ruby Objects (POROs) which encapsulate a whole
business process/user interaction.
Our services are located in `app/services` with the corresponding specs in
`spec/services`. Their main interface is a class level method named `call`, as
in the following example:
```ruby
class ImportUsers
def self.call(arg1)
new(arg1).call
end
def initialize(arg1)
@arg1 = arg1
end
def call
# import code goes here
end
end
```
To distinguish services from models we often give them verb names vs noun names,
e.g. `ImportUsers` instead of `UserImporter`.
## Generating Service Objects
To make our services more consistent we use a custom Rails generator. Some usage
examples:
**Generate a non-namespaced service without arguments**
`$ rails generate service DoTheThing`
```ruby
# app/services/do_the_thing.rb
class DoTheThing
def self.call
new.call
end
def call
end
end
```
```ruby
# spec/services/do_the_thing_spec.rb
require "rails_helper"
RSpec.describe DoTheThing, type: :service do
pending "add some examples to (or delete) #{__FILE__}"
end
```
**Generate a non-namespaced service with arguments:**
`$ rails generate service DoTheThing arg1 arg2`
```ruby
# app/services/do_the_thing.rb
class DoTheThing
def self.call(arg1, arg2)
new(arg1, arg2).call
end
def initialize(arg1, arg2)
@arg1 = arg1
@arg2 = arg2
end
def call
end
end
```
The generated spec is the same as above.
**Generate a namespaced service with arguments**
`$ rails generate service things/dothem arg1 arg2`
```ruby
# app/services/things/do_them.rb
class Things::DoThem
def self.call(arg1, arg2)
new(arg1, arg2).call
end
def initialize(arg1, arg2)
@arg1 = arg1
@arg2 = arg2
end
def call
end
end
```
```ruby
# spec/services/things/do_them_spec.rb
require "rails_helper"
RSpec.describe Things::DoThem, type: :service do
pending "add some examples to (or delete) #{__FILE__}"
end
```

View file

@ -1,73 +0,0 @@
---
title: Tracking
---
## Visits & Events
For first-party analytics, we use the
[`ahoy_matey` gem](https://github.com/ankane/ahoy), which tracks visits and
events.
We intentionally choose to limit what user data we track and persist, and have
opted to follow the GDPR compliance standards
[set by Ahoy](https://github.com/ankane/ahoy#gdpr-compliance-1). By default, we
have configured the Ahoy library to mask IP addresses, disable geocode tracking,
and not track user cookies.
### Visits
Ahoy creates an `Ahoy::Visit` record for each visit that it tracks.
By default, we have turned off visit tracking in the `ApplicationController`:
```ruby
skip_before_action :track_ahoy_visit
```
We currently only create visits on the server-side when they are required to be
created by events. Visits can be re-enabled for specific controller actions if
necessary, but this should be done so _with explict care_.
We do not collect any personal user data when tracking visits. Our collected
data is limited to the user's `id`. Each user has a unique `visitor_token`,
while each visit to the site is marked with a unique `visit_token`.
### Events
Ahoy creates an `Ahoy::Event` record for each event that it tracks. If no visit
is recorded for a user when an event is tracked, Ahoy will simultaneously create
an `Ahoy::Visit` for the event being tracked.
Events can be tracked in a controller action on the backend, or with JavaScript
on the frontend. Learn more about tracking events with JavaScript in our
[frontend tracking guide](/frontend/tracking).
When an event is tracked, it should include a `name` and a `properties` hash.
When adding new events, be sure that the name is unique per-event. The
properties will help you differentiate between events.
In order to track a specific event in a controller, use the `ahoy.track` call:
```ruby
class YourController < ApplicationController
after_action :track_my_action
protected
def track_my_action
ahoy.track "A specific description of your event", request.path_parameters
end
end
```
Event tracking can be enabled for specific controller actions, but should be
done so _with explict care_.
## Messages
For email analytics, we use the
[`ahoy_messages` gem](https://github.com/ankane/ahoy_email), which tracks a
history of email messages sent to users.
Ahoy creates an `Ahoy::Message` record for each email sent by default, but can
be disabled on a per-mailer basis.

View file

@ -1,52 +0,0 @@
---
title: Contributing to the API Specification Docs
---
# Contributing to API specification docs
The API v0 is described with the
[OpenAPI 3 specification](https://spec.openapis.org/oas/v3.0.3).
Swagger.io has
[great docs](https://swagger.io/docs/specification/basic-structure/) that are
helpful to understand the specification better.
## Where you can find the spec file
We auto-generate the documentation from `api_v0.yml` within the `/docs`
directory. We use [ReDoc](https://github.com/Redocly/redoc) to turn the OpenAPI
3 format into a readable and searchable HTML documentation.
## Updating API docs
Whenever you make changes to the API docs, make sure to bump the version at the
top of `api_v0.yml`, in `info.version`.
## Running and editing the docs locally
If you want to browse the documentation locally you can use:
```shell
yarn api-docs:serve
```
This will let you browse the auto-generated version of the doc locally, and it
will reload the documentation after every change of the specification file.
## Linting and validation
We use [spectral](https://github.com/stoplightio/spectral) and
[ibm-openapi-validator](https://github.com/IBM/openapi-validator) to validate
the spec file. The validation is performed as a `pre-commit` hook.
You can also manually validate the document, running:
```shell
yarn api-docs:lint
```
If you have Visual Studio Code, we suggest you install the following extensions
that enable validation and navigation within the spec file:
- [OpenAPI (Swagger) editor](https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi)
- [openapi-designer live preview](https://marketplace.visualstudio.com/items?itemName=philosowaffle.openapi-designer)

View file

@ -1,77 +0,0 @@
---
title: Contributing to the Docs
---
# Contributing to Forem's developer documentation
Contributions to the documentation are always appreciated! Thank you for making
an effort to improve the developer experience of contributing to the DEV
project.
# Running the documentation locally
Like Forem, this site is open source and the code is [hosted on GitHub][docs].
If you find any incorrect information, or a even a typo, we'd love to see a pull
request. Follow these steps to get the documentation site running locally.
Forem's documentation is built with [GitDocs NodeJS library][gitdocs].
Once installed, you should run `yarn gitdocs serve` from the root of the `forem`
project or from the `/docs` directory.
```shell
yarn gitdocs serve
```
This will start a server where you can browse the documentation:
<http://localhost:8000/>
If you add new pages or rename existing pages, you'll need to restart the server
for those changes to take effect.
Since our documentation is built on GitDocs, which is built on Netlify, you can
use the generated deploy preview link to check out your documentation changes.
Once you make a PR, click on "Show all checks" and find the "deploy/netlify"
row. If your deploy preview is ready, you can click on "Details" to see the
preview. Please note that the deploy preview only reflects any documentation
changes you make (and not any changes elsewhere in the app).
# Useful links
The docs are a collection of [Markdown files][markdown] that also utilize
[FrontMatter][frontmatter].
For more information on how to use GitDocs read the [GitDocs
guide][gitdocs_guide].
# Regarding language & style
We ask that you avoid trivializing terms when contributing to documentation.
This includes words like "just", "simply", "easy", "obvious", and
"straightforward". You can learn more about why we want to avoid this kind of
language in [this blog post](https://jessitron.com/2020/06/26/just-dont).
Generally speaking, the documentation hosted on this site is informal. There is
no need to make things more complicated by writing these articles like a
textbooks.
However, it's expected that contributions to these documents are reasonably
structured and mostly free of spelling and grammar errors. For this reason, if
you submit a PR you might be asked to make changes before your PR is merged.
Prettier is used to autowrap lines in these files to 80 characters. Using 80
characters per line allows us to retain a more specific git history over time.
If lines are not wrapped, changing a comma in a paragraph would attribute the
entire paragraph to one commit. By line wrapping we are helping git to correctly
attribute smaller changes to their commits. This keeps information from getting
lost over time.
For more information on effective technical writing, check out
[writethedocs.org][writethedocs].
[docs]: https://github.com/forem/forem/tree/main/docs/
[gitdocs]: https://www.npmjs.com/package/gitdocs/
[markdown]: https://en.wikipedia.org/wiki/Markdown
[frontmatter]: https://jekyllrb.com/docs/front-matter/
[gitdocs_guide]: https://gitdocs.netlify.com/
[writethedocs]: https://www.writethedocs.org/guide/

View file

@ -1,202 +0,0 @@
---
title: Contributing to Forem
---
# Contributing to Forem
We expect contributors to abide by our underlying
[Code of Conduct](https://dev.to/code-of-conduct). All discussions about this
project must be respectful and harassment-free.
Remember that communication is the lifeblood of any Open Source project. We are
all working on this together, and we are all benefiting from this software.
It's very easy to misunderstand one another in asynchronous, text-based
conversations. When in doubt, assume everyone has the best intentions.
If you feel anyone has violated our Code of Conduct, you should anonymously
contact the team with our [abuse report form](https://dev.to/report-abuse).
### Where to contribute
All [issues](https://github.com/forem/forem/issues) labeled
[ready for dev](https://github.com/forem/forem/issues?q=is%3Aissue+is%3Aopen+label%3A%22ready+for+dev%22)
and
[bug](https://github.com/forem/forem/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+bug%22+label%3Abug)
are up for grabs. Please note that issues with the
[Forem team](https://github.com/forem/forem/labels/Forem%20team) label are
internal tasks that will be completed by a Forem
[core team member](https://github.com/forem/forem/#core-team).
- [good first issue](https://github.com/forem/forem/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+)
issues are meant for newer developers.
- [difficulty: easy](https://github.com/forem/forem/issues?q=is%3Aopen+is%3Aissue+label%3A%22difficulty%3A+easy%22)
issues are usually confined to isolated areas of existing code.
- [difficulty: medium](https://github.com/forem/forem/issues?q=is%3Aopen+is%3Aissue+label%3A%22difficulty%3A+medium%22)
issues sometimes entail new features and might affect a significant area of
the codebase, but aren't overly complex.
- [difficulty: hard](https://github.com/forem/forem/issues?q=is%3Aopen+is%3Aissue+label%3A%22difficulty%3A+hard%22)
issues are typically far-reaching, and might need architecture decisions
during implementation. This label might also denote highly complex issues.
PRs without an associated issue may still be merged, but the core team will
focus on changes that solve existing issues. We strongly encourage creating an
issue before working on a PR!
When in doubt, ask a
[core team member](https://github.com/forem/forem/#core-team) by mentioning us
on the issue.
**Documentation** is almost always a great place to start contributing to a new
project. Forem is an Open Source, community-driven project. Therefore, providing
and maintaining quality documentation is one of our most important jobs. You can
find more information in our
[docs guide](https://docs.forem.com/contributing/docs)!
**Refactoring**, which involves improving the code without modifying behavior,
is a great place to help out! Generally speaking, you can rely on existing tests
to ensure that your refactor doesn't introduce any unexpected behavior. If an
area isn't well tested, you might be asked to include a regression test with
your refactoring PR. Refactors can touch many files, so we encourage breaking
big changes into small PRs.
**Fixing bugs** is a super fast way to improve the experience for our users!
When you're fixing bugs, we appreciate communication in a GitHub issue. If an
issue exists, please claim that issue and link it in your PR, otherwise creating
an issue is the best first step! Be sure to surround bug fixes with ample tests;
bugs are magnets for other bugs. Write tests around bugs!
**Features** tend to be subjective and might spur some debate. The Forem core
team uses an internal RFC ("request for comments") process to assess and
prioritize new features. This process is intended to provide a consistent and
standardized path for new changes to enter the Forem ecosystem. If you'd like to
propose a new feature, please visit [forem.dev](https://forem.dev) to start a
discussion around a new feature (or chime in on a pre-existing discussion!).
There may be some open issues in our repository that we think evolve into
impactful features. For such issues, we use the
[potential RFC](https://github.com/forem/forem/labels/potential%20RFC) label in
order to highlight the potential feature to the Forem core team members so that
someone from the team can champion that feature.
You can learn more about our internal RFC process and how we use forem.dev
[here](https://forem.dev/foremteam/internal-rfc-process-and-forem-dev-discussions-3gl4)
### How to contribute
1. [Fork the project](https://docs.forem.com/getting-started/forking/) and clone
it to your local machine. Follow the
[installation guide](https://docs.forem.com/installation/)!
2. Create a branch with your GitHub username and the ID of the
[issue](https://github.com/forem/forem/issues), for example:
`git checkout -b USERNAME/some-new-feature-1234`
3. Code and commit your changes. Bonus points if you write a
[good commit message](https://chris.beams.io/posts/git-commit/):
`git commit -m 'Add some feature'`
4. Push to the branch: `git push -u origin USERNAME/some-new-feature-1234`
5. [Create a pull request](https://docs.forem.com/getting-started/pull-request/)
for your branch. 🎉
## Contribution guidelines
### Create an issue
Nobody's perfect. Something doesn't work? Something could be better? Check to
see if the issue already exists, and if it does, leave a comment to get our
attention! If the issue doesn't already exist, feel free to create a new one. A
core team member will triage incoming issues.
_Please note: core team members may update the title of an issue to reflect the
discussion._
### Please include tests
Some areas of the project could use updated tests, and new features should
always include test coverage. Please give our
[testing guide](https://docs.forem.com/tests/) a read!
### Code quality
We use [Code Climate](https://codeclimate.com/) to find code smells. If a pull
request contains code smells, we might recommend a refactor before merging. We
like readable code, and encourage DRY when it's reasonable!
More importantly, we avoid
[wrong abstractions](https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction).
Code quality tools are not perfect, so don't obsess over your Code Climate
score.
### Consider accessibility in UI changes
If the change you're proposing touches a user interface, include accessibility
in your approach. This includes things like color contrast, keyboard
accessibility, screen reader labels, and other common requirements. For more
information, check out the
[Forem Accessibility docs page](https://docs.forem.com/frontend/accessibility).
### Please use inclusive language
Inclusion and respect are core tenets of our
[Code of Conduct](https://dev.to/code-of-conduct). We expect thoughtful language
all the way down to the code. Some technical metaphors are alienating or
triggering. We ask that contributors go the extra mile to submit code which is
inclusive in nature.
If you unintentionally use language deemed harmful, there is no shame. We will
work together to find a better alternative. Being thoughtful about language also
encourages more thoughtful code!
### Create a pull request
- Try to keep the pull requests small. A pull request should try its very best
to address only a single concern.
- For work in progress pull requests, please use the
[Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/)
feature.
- Make sure all tests pass and add additional tests for the code you submit.
[More info here](https://docs.forem.com/tests/).
- Document your reasoning behind the changes. Explain why you wrote the code in
the way you did. The code should explain what it does.
- If there's an existing issue, reference to it by adding something like
`References/Closes/Fixes/Resolves #123`, where 123 is the issue number.
[More info here](https://github.com/blog/1506-closing-issues-via-pull-requests).
- Please fill out the PR Template when making a PR.
- All commits in a pull request will be squashed when merged.
_Please note: a core team member may close your PR if it has gone stale or if we
don't plan to merge the code._
### Pull request reviews
All community pull requests are reviewed by our core team.
- All contributors must sign the CLA.
- All required checks are expected to pass on each PR.
- In the case of flaky or unrelated test failures, a core team member will
restart CI.
- We require 2 approvals from core team members for each PR.
- Requested Changes must be resolved (with code or discussion) before merging.
- If you make changes to a PR, be sure to re-request a review.
- Style discussions are generally discouraged in PR reviews; make a PR to the
linter configurations instead.
- Your code will be deployed shortly after it is merged.
### A note on "force pushing"
After you submit your pull request, one of the members of the core team will
review your code.
Please avoid force pushing unless you need to rebase with the main branch.
If feedback is provided, any changes should be contained in new commits. Please
don't force push or worry about squashing your commits.
Force pushing (despite being useful) has some drawbacks. GitHub doesn't always
keep the review history, which results in lost context for the reviewers.
We squash every PR before merging, so there is no need to force push!
## The bottom line
We are all humans trying to work together to improve the community. Always be
kind and appreciate the need for tradeoffs. ❤️

View file

@ -1,9 +0,0 @@
---
title: Contributing Guide
items:
- docs.md
- forem.md
- api.md
---
# Contributing Guide

View file

@ -1,96 +0,0 @@
---
title: Configuring Forem
---
# Basic Configuration Guide for Forem
## Overview
As a Forem admin, one of the first steps of managing your site will be to tailor
your content, branding and other important details based on your community
goals. This is made simple by the configuration page which can be found at
https://<span></span>your.forem.url/admin/customization/config.
_We advise that you first complete the minimum set up before sending out your
Forem link to your community._
## Complete your Set Up, Configure your Forem
Once your Forem instance is set up for the first time, you will most likely see
the following banner:
![Banner prompting for configuring mandatory settings](https://dev-to-uploads.s3.amazonaws.com/i/2nosvfr7l47ymipmyh4o.png)
This banner indicates that the Forem configuration process hasn't been completed
yet.
When you click on the link on banner, it will take you to the configuration page
(i.e. https://<span></span>your.forem.url/admin/customization/config).
On this page you will see that the Get Started section is expanded. It contains
all the mandatory fields that need to be filled out in order for the site to be
in a usable state. Once it is filled out and submitted, the banner will then
disappear.
## Access and Permissions
The following permissions are required to be able to view and/or edit the config
page:
#### `Role: super_admin`
When providing this role to a user they will be able to access the config page.
However, this page will be a read only view for them. They will see the
following:
![Super Admin Permissions Role Provided](https://dev-to-uploads.s3.amazonaws.com/i/xpc8g9x46vzgi49ohc0d.png)
#### `Role: single_resource_admin`
When providing this role to a user they will be able to access the config page,
and they will be able to edit the config variables.
![Super Resource Admin Role Provided](https://dev-to-uploads.s3.amazonaws.com/i/z5v2ou64imgqonmefolk.png)
The first admin of your Forem will be given the highest permissions. Thereafter,
they can provide the necessary permissions to subsequent users via the
https://<span></span>your.forem.url/admin/permissions.
## The Configuration Sections
Currently, the configuration page is split into 3 sections. They are as follows:
- A **Get Started section** that contains all required fields. These are the
fields that are required to be filled out, in order to get your Forem in a
usable state.
- An **All Settings section** that contains all the possible variables that you
can configure on the site. This section is broken down into sub sections,
whereby each subsection will contain a description of what it does, and then
list the fields that are available for configuration. Each field will also
contain a concise description of what it is used for.
- An **Environment Variables section** that provides a read-only view of the
environment variables that are available to be set on the server. If your
instance is hosted by Forem, please get in touch with customer support to
change any of these variables.
![The Settings Sections](https://user-images.githubusercontent.com/47985/116500126-13970a00-a8d8-11eb-824e-ace66e6e05d4.png)
All required fields are marked as such. In addition, you will notice that we
have set some defaults for certain fields, you may amend them as you see
relevant for your Forem.
## Updating your configurations
In order to update any of the variables within the Get Started and All Settings
sections, you will set the new value and then navigate to the end of the section
where you will verify that you would like to make the change by typing the
following sentence:
```
My username is <specify your username here> and this action is 100% safe and appropriate.
```
You will now see your updated values and the changes on your site will be in
effect.
![Submit your data](https://dev-to-uploads.s3.amazonaws.com/i/xo0nxykuu8kw984w088n.png)

View file

@ -1,9 +0,0 @@
---
title: Forem Creators Guide
items:
- configure-forem.md
---
# Forem Creators Guide
This document contains basic instructions for new Forem creators. It's a work in-progress, but will provide Forem Creators with a guide on how to best make use of the tools and features available to them.

View file

@ -1,40 +0,0 @@
---
title: Branding Guidelines
---
# Branding Guidelines
## Forem Theme
Forem's design inspiration is meant to be a throwback. In personality and
design, it is ATARI inspired. Magazines like PAPER are also an inspiration in
terms of being a
[bold contrast with some more indy](https://www.google.com/search?biw=1440&bih=780&tbm=isch&sa=1&ei=KSN8W5WVLoy55gLI77TgBA&q=paper+magazine+cover&oq=paper+magazine+cover).
We also think in terms of how people use the Forem brand for their own purposes.
Profiles are very much public-facing records of what people have put out into
the world. `dev.to/{username}` is your developer identity.
Imagine a developer's face with the DEV heading above them. If we existed in the
80's, we would have been "DEV MAGAZINE," with the exact same branding.
## Branding Rules
- [Joystick](http://www.pixelsagas.com/?download=joystick) should be used for
logos, not for headlines/phrases.
- Black and white is our default logo, but the logo can be drawn in any color.
- Forem/DEV is "old school cool."
- We use bold shadows (no gradient) for boxes.
- We have a minimal approach to importing dependencies, so few if any custom
fonts on-site for that reason.
- Refer to the `variables.scss`
[file in our main repo](https://github.com/forem/forem/blob/main/app/assets/stylesheets/variables.scss)
to reference commonly-used colors and fonts.
## Design License Info
- We use the [Joystick](http://www.pixelsagas.com/?download=joystick) font by
Neale Davidson and [Pixel Sagas](http://www.pixelsagas.com/) for the DEV Logo.
We have a commercial license.
- We use [EmojiOne](https://www.emojione.com/) icons from JoyPixels. We have a
commercial license.

View file

@ -1,41 +0,0 @@
# Human Language
Forem is built on concepts, and those concepts have names. Here are some language concepts to be aware of and some known "gotchas" where there may be multiple terms currently being used.
This is an incomplete first take. It is also written down _so that it can be changed when needed._ No language concept is set in stone. And no concept is exclusively _english_. As we internationalize we need to evolve our notion of this, and it will remain important that we settle on unified terms so that we limit conceptual sprawl.
# Post
A main published unit on sites are officially called posts. The gotcha is that they are currently called `Article` in the codebase. This is because the original version of the site had much more the concept of the pure "article" but as the site has evolved and the purposes of Forem have evolved, we have diversified the use of a post idea. Just as a "tweet" could be used for different things: An announcement, a question, the start of a thread, etc. we also have a broad idea of what a post is "for", and all concepts are built off of this.
# Comment
A single comment is the unit of follow up dialog that lives underneath a post. Comments have some associated terminology.
- A Discussion is the collection of all comments under a post. Discussion is a word that implies a certain civility.
- A thread is a single line of comments and their reply comments within a full discussion.
- A comment within a thread may be thought of as a "reply" contextually, but it is still a comment.
# Reaction
The remotional responses to an article or comment, a click of a button to let the author know how one feels. The phrase "reaction" is used in the app, but should mostly be _implied_. Reactions are purposefully lightweight in nature.
# Tag
A tag is the main unit of organized content on a Forem.
# User
A signed up account is generically referred to as a "user", but contextually we've called them "members" or "people", there may be some need for unification here.
# Listing
The marketplace/classified-listing concept within a forem is called `/listings` and a single unit is a listing. Listings have categories for segmentation as well as tags, like posts do.
# Connect
Connect is the area of the site for synchronous conversation, closed group chat and/or video or audio conversation. It's generally not as site-wide or public, though they _can_ be public in some contexts.
# Connect Channel
A single unit of chat is called a "channel".

View file

@ -1,9 +0,0 @@
---
title: Design Guide
items:
- branding.md
- language.md
- theming.md
---
# Design Guide

View file

@ -1,85 +0,0 @@
---
title: Theming Guidelines
---
# Theming Guidelines
Forem supports different themes, such as Default, Night, Pink.
You can switch the theme at <http://localhost:3000/settings/ux> in the "Style
Customization" section.
These themes are powered by
[CSS custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties),
loaded at runtime by JavaScript via `layouts/_user_config.html.erb`.
An example of how it works:
```javascript
try {
const bodyClass = localStorage.getItem('config_body_class');
if (bodyClass) {
document.body.className = bodyClass;
if (bodyClass.includes('night-theme')) {
document.getElementById('body-styles').innerHTML = '<style><%= Rails.application.assets["themes/night.css"].to_s.squish.html_safe %></style>';
} else if (bodyClass.includes('ten-x-hacker-theme')) {
document.getElementById('body-styles').innerHTML = '<style><%= Rails.application.assets["themes/hacker.css"].to_s.squish.html_safe %></style>'
} else if (bodyClass.includes('pink-theme')) {
document.getElementById('body-styles').innerHTML = '<style><%= Rails.application.assets["themes/pink.css"].to_s.squish.html_safe %></style>'
} else if(bodyClass.includes('minimal-light-theme')) {
document.getElementById('body-styles').innerHTML = '<style><%= Rails.application.assets["themes/minimal.css"].to_s.squish.html_safe %></style>'
}
...
```
Within SCSS files located at `app/assets/stylesheets` you can use `var()`
statements, which will call these CSS custom properties. If a CSS custom
property is not defined `var()` will fall back on the second parameter provided,
which can be a SCSS variable defined in `app/assets/stylesheets/variables.scss`.
An example of how to use `var()`:
```scss
div {
color: var(--theme-color, $black);
}
```
Note that fallback values aren't used to fix the browser compatibility. If the
browser doesn't support CSS custom Properties, the fallback value won't help. To
prevent this issue on older browsers, we need to write our styles like this:
```scss
div {
color: $black;
color: var(--theme-color, $black);
}
```
This can be too much work and browser fallback can be forgotten. For a better
developer experience, you should use the two mixins defined in
`app/assets/stylesheets/_mixins.scss` called `themeable` and
`themeable-important`. They take three arguments. The first argument is the CSS
property like `color` or `background`, the second argument is the CSS custom
property name (without `--`) like `theme-color`, and the third argument is the
CSS value, i.e., `$black` or `white`.
Make sure to import the mixin in your SCSS file and use it like this:
```scss
div {
@include themeable(color, theme-color, $black);
}
```
`themeable-important` is used when a CSS variable requires `!important` as a
postfix of the CSS property's value where the CSS variable is being used. You
can use it the same way you would use `themeable` mixin, but you should avoid
`!important` if possible.
# Other user config
In addition to themes, users can also directly configure their preferred fonts
and their nav bar preferences. The implementation of these is similar to themes.

View file

@ -1,97 +0,0 @@
---
title: FAQs
---
# Frequently Asked Questions
## How do I log in after starting up Forem for the first time?
Seeding the database create an admin user (see
[Database](/getting-started/db/#default-admin-user)) with the following
credentials:
```
email: admin@forem.local
password: password
```
Once logged in as this admin user, you can turn on any authentication methods
you'd like (see [Authentication](/backend/authentication/))
## How do I build my local copy of the Ruby source code documentation?
```shell
cd docs
make ruby-doc
```
Then open `.static/ruby-doc/index.html` in the `docs` directory and browse the
Ruby documentation
## How do I enable logging to standard output in development?
By default Rails logs to `log.development.log`.
If, instead, you wish to log to `STDOUT` you can add the variable:
```shell
export RAILS_LOG_TO_STDOUT=true
```
to your own `.env` file.
## How do I see comments in the Feed?
On the home Feed, we only show comments above certain "score". It's likely the
comments in the local environment will never meet this score. If you want to see
comments locally, you will need to update the score of your local comments
manually. Here's how:
1. Open the terminal.
2. Run `rails dbconsole` to open the PostgreSQL terminal. Alternatively, run
`psql PracticalDeveloper_development` to open `psql`, the PostgreSQL
terminal.
3. Enter `update comments set score = 30;`.
4. Type `exit` to leave the PostgreSQL terminal.
> Note: dbconsole reads database information from config/database.yml which is
> always better since database configs might change in the future.
Once you refresh the app, you should be able to see some comments in the Feed.
## How do I make someone follow me on my local environment?
In certain cases, for example when testing various functionalities, you may need
to be able to make some user follow you. Here's how:
1. Open the rails console by running `rails c` in your terminal.
2. Get any user you want to follow you, for example `user = User.first`.
3. Then make this user follow you: `user.follow(your_username)`.
Boom, you have a new follower!
## How do I remove / leave organization I created?
1. Open the rails console by running `rails c` in your terminal.
2. Enter the following commands:
```ruby
user = User.find_by(username: "your_username")
organization_id = Organization.find_by(slug: "organization_slug").id
user.organization_memberships.where(organization_id: organization_id).destroy_all
```
## How do I add credits to my account?
If you ever want to add Listings locally, you must have credits on your account
to "pay" for listing. Here's how:
1. Open the rails console `rails console`.
2. Enter the following commands:
```ruby
user = User.find_by(username: "your_username")
Credit.add_to(user, 1000)
```
^ This will add 1000 credits to your account. But you know, you can't really buy
anything with it :D

View file

@ -1,54 +0,0 @@
---
title: Accessibility
---
# Accessibility
To make Forem the most inclusive community platform around, accessibility should
be considered to enable people with disabilities to create and consume content.
## The Basics
Forem UI changes should consider accessibility wherever possible. Common issues
to watch out for in frontend code:
- [Adequate color contrast](https://webaim.org/articles/contrast/evaluating)
- [Semantic structure and headings](https://webaim.org/techniques/semanticstructure/)
- [Alternative text for images](https://webaim.org/techniques/alttext/)
- [Unique button and link text](https://webaim.org/techniques/hypertext/link_text)
- [Accessible forms with labels](https://webaim.org/techniques/forms/)
- [Visible keyboard focus styles](https://www.washington.edu/accessibility/checklist/focus/)
## More Advanced Things
If you're working on something JavaScript-heavy or animated, there are a few
additional considerations for accessibility:
- [Forem Accessibility Tests](https://docs.forem.com/tests/accessibility-tests/)
- [Intro to ARIA](https://webaim.org/techniques/aria/)
- [Handle focus for client-side interactions](https://dev.to/robdodson/managing-focus-64l)
- [Reducing motion with CSS media queries](https://css-tricks.com/introduction-reduced-motion-media-query/)
- [Linting with eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y)
- [Testing with Jest-axe](https://dev.to/bdougieyo/accessibility-testing-in-react-with-jest-axe-l7k)
## Accessibility Testing
See a list of testing steps to follow during development or for a Pull Request
review on the
[Forem Accessibility Testing Docs](https://docs.forem.com/tests/accessibility-tests/).
## Resources
There's a wealth of information out there to learn about digital accessibility!
Here are some resources:
- [W3C's Web Accessibility Initiative](https://www.w3.org/WAI/)
- [Web Content Accessibility Guidelines](https://www.w3.org/TR/WCAG21/)
- [ARIA Authoring Practices](https://www.w3.org/TR/wai-aria-practices-1.1/)
- [WebAIM](http://webaim.org/)
- [A11y Project](https://a11yproject.com)
- [Deque University](https://dequeuniversity.com/)
- [React Accessibility Docs](https://reactjs.org/docs/accessibility.html) (most
will apply to Preact)
- [The Importance of Manual Accessibility Testing](https://www.smashingmagazine.com/2018/09/importance-manual-accessibility-testing/)
- [Accessibility Insights extension](https://accessibilityinsights.com)

View file

@ -1,52 +0,0 @@
---
title: Debugging
---
# Debugging
## In Browser
Browsers ship with their own developer tools. These are amazing tools to help
you debug your web application. Consider learning how to use them.
- [Chrome Developer Tools](https://developers.google.com/web/tools/chrome-devtools)
- [Firefox Developer Tools](https://developer.mozilla.org/en-US/docs/Tools)
- [Safari Developer Tools](https://support.apple.com/en-ca/guide/safari/sfri20948/mac)
## Visual Studio Code
[Visual Studio Code](https://code.visualstudio.com) (VS Code) is a popular
editor that allows you to debug many languages including JavaScript. Thanks to
remote debugging technologies, we can debug our frontend code in VS Code. When
you clone the Forem repository and open the project in VS Code, you will be
prompted to install recommended extensions which include the
[Chrome Debugger](https://code.visualstudio.com/blogs/2016/02/23/introducing-chrome-debugger-for-vs-code)
and the
[Edge Debugger](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-edge).
Setup:
- Refer to the respective debugger extension documentation above to ensure that
your browser is running with remote debugging enabled.
- Once you have your local installation of Forem running, you can attach to
either the Chrome or Edge Debugger.
![Launch menu for debugger in VS Code](/vscode_launch_debugger.png 'Launch
menu for debugger in VS Code')
- From there you can do all the usual stuff that you would do while debugging
JavaScript in the browser: setting breakpoints, setting
[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints),
watches etc.
## Where is My Editor Debug Configuration?
If you do not see your editor here, consider contributing to the documentation.
😉
## Preact Developer Tools
Preact has their
[own developer tools](https://preactjs.github.io/preact-devtools/) in the form
of a browser extension. The Forem codebase is configured out of the box to
support the Preact Devtools.

View file

@ -1,34 +0,0 @@
---
title: Dynamic Imports
---
# Dynamic Imports
[Dynamic imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import/#Dynamic_Imports)
are supported in all major browsers except for Edge
([EdgeHTML](https://en.wikipedia.org/wiki/EdgeHTML) version) and Internet
Explorer which are both unsupported browsers for Forem. They allow you to import
JavaScript dynamically instead of statically. Why is this important?
Performance.
Should you use them everywhere? No. They are a great tool when you need to load
a JavaScript module on the fly for functionality that is not needed immediately
for the page to be usable.
Here are a couple of examples of dynamic import usage on Forem:
- The
[Onboarding flow](https://github.com/forem/forem/blob/0633d85b6b0e083bb7b21b11642b2b17d3fe9de6/app/javascript/packs/Onboarding.jsx#L21).
- In
[homepage](https://github.com/forem/forem/blob/0633d85b6b0e083bb7b21b11642b2b17d3fe9de6/app/javascript/packs/homePage.jsx#L59)
(followed tags).
Forem uses [webpacker](frontend/webpacker) (webpack), so what webpack will do is
create separate bundles for code that is dynamically imported. So not only do we
end up loading code only when we need it, we also end up with smaller bundle
sizes in the frontend.
For a great deep dive into dynamic imports, there is a great article from
community member [@goenning](https://dev.to/goenning) about dynamic import
usage,
[How we reduced our initial JS/CSS size by 67%](https://dev.to/goenning/how-we-reduced-our-initial-jscss-size-by-67-3ac0).

View file

@ -1,43 +0,0 @@
# InstantClick
Like the [InstantClick](http://instantclick.io/) tag line says, “InstantClick is
a JavaScript library that dramatically speeds up your website, making navigation
effectively instant in most cases.”.
The way it works is if a user hovers over a hyperlink, chances are their
intentions are to click on it. InstantClick will start prefetching the page
while a user is hovering over a hyperlink, so that by the time they do click on
it, it's instantaneous. On mobile devices, preloading starts on
[touchstart](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchstart_event).
Aside from prefetching pages, InstantClick also allows you to customize what
happens when an InstantClick page changes.
```javascript
// Found in https://github.com/forem/forem/blob/main/app/javascript/packs/githubRepos.jsx#L11)
window.InstantClick.on('change', () => {
loadElement();
});
```
You can also decide whether or not to reevaluate a script in an InstantClick
loaded page via the `data-no-instant` attribute.
```javascript
// Found in https://github.com/forem/forem/blob/main/app/assets/javascripts/utilities/buildCommentHTML.js.erb#L80
function actions(comment) {
if (comment.newly_created) {
return '<div class="actions" data-comment-id="'+comment.id+'" data-path="'+comment.url+'">\
<span class="current-user-actions" style="display: '+ (comment.newly_created ? 'inline-block' : 'none') +';">\
<a data-no-instant="" href="'+comment.url+'/delete_confirm" class="edit-butt" rel="nofollow">DELETE</a>\
<a href="'+comment.url+'/edit" class="edit-butt" rel="nofollow">EDIT</a>\
</span>\
<a href="#" class="toggle-reply-form" rel="nofollow">REPLY</a>\
</div>';
} else {
...
```
For more information on this, see the
[Events and script re-evaluation in InstantClick](http://instantclick.io/scripts)
documentation.

View file

@ -1,33 +0,0 @@
---
title: Linting and Formatting
---
# Linting and Formatting
The project uses [eslint](https://eslint.org/) with the
[Prettier plugin](https://github.com/prettier/eslint-plugin-prettier). eslint
handles linting, but eslint rules related to code formatting, they get handled
by prettier. For the most part, out of the box rules provided by the
configurations that are extended are used but there are some tweaks.
Forem also has some objects that live in the global scope, e.g. Pusher. The
eslint globals section of the eslint configuration is what enables these to be
reported as existing when eslint runs.
```javascript
globals: {
InstantClick: false,
filterXSS: false,
Pusher: false,
}
```
## Husky and lint-staged
The code base uses a pre-commit hook that is enabled by the
[husky](https://github.com/typicode/husky) and
[lint-staged](https://github.com/okonet/lint-staged) tools. The pre-commit hook
runs eslint before frontend code is committed. If there are any issues that can
automatically be fixed, eslint will fix them. If there are linting issues that
cannot be resolved, the commit fails and the changes need to be handled
manually. Prettier also runs during the pre-commit hook.

View file

@ -1,160 +0,0 @@
---
title: Liquid Tags
---
# Liquid Tags
Liquid tags are special elements of the
[Forem Markdown editor](https://dev.to/new).
They are custom embeds that are added via a `{% %}` syntax.
[Liquid](https://shopify.github.io/liquid/) is a templating language developed
by Shopify.
Liquid embeds are for tweets, like `{% tweet 765282762081329153 %}` or a Forem
user profile preview, like `{% user jess %}` etc.
They make for good community contributions because they can be extended and
improved consistently. It is truly how we extend the functionality of the
editor. At the moment, there could be a lot of work refactoring and improving
existing liquid tags, in addition to adding new ones.
Liquid tags are sort of like functions, which have a name and take arguments.
Develop them with that mindset in terms of naming things. They should be
documented but also intuitive. They should also be fairly flexible in the
arguments they take. Currently, this could use improvements.
_Note: Liquid tags are "compiled" when an article is saved. So you will need to
re-save articles to see HTML changes._
Here is a bunch of liquid tags supported on Forem:
```liquid
{% link https://dev.to/kazz/boost-your-productivity-using-markdown-1be %}
{% user jess %}
{% tag git %}
{% devcomment 2d1a %}
{% podcast https://dev.to/basecspodcast/s2e2--queues-irl %}
{% twitter 834439977220112384 %}
{% glitch earthy-course %}
{% github forem/forem %}
{% youtube dQw4w9WgXcQ %}
{% vimeo 193110695 %}
{% twitch ClumsyPrettiestOilLitFam %}
{% slideshare rdOzN9kr1yK5eE %}
{% codepen https://codepen.io/twhite96/pen/XKqrJX %}
{% stackblitz ball-demo %}
{% codesandbox ppxnl191zx %}
{% jsfiddle https://jsfiddle.net/link2twenty/v2kx9jcd %}
{% dotnetfiddle https://dotnetfiddle.net/PmoDip %}
{% replit @WigWog/PositiveFineOpensource %}
{% stackery deeheber lambda-layer-example layer-resource %}
{% nexttech https://nt.dev/s/6ba1fffbd09e %}
{% instagram BXgGcAUjM39 %}
{% speakerdeck 7e9f8c0fa0c949bd8025457181913fd0 %}
{% soundcloud https://soundcloud.com/user-261265215/dev-to-review-episode-1 %}
{% spotify spotify:episode:5V4XZWqZQJvbddd31n56mf %}
{% blogcast 1234 %}
{% kotlin https://pl.kotl.in/owreUFFUG %}
{% wikipedia https://en.wikipedia.org/wiki/Wikipedia %}
{% reddit https://www.reddit.com/r/aww/comments/ag3s4b/ive_waited_28_years_to_finally_havr_my_first_pet %}
```
## How liquid tags are developed
Liquid tags are a matter of parsing the "arguments" and serving relevant
JavaScript.
Liquid tags go in the `app/liquid_tags` folder. All liquid tags inherit from the
base, like so...
```ruby
class KotlinTag < LiquidTagBase
```
Each liquid tag contains an `initialize` method which takes arguments and calls
`super`. It also has a `render` method which calls the appropriate view.
```ruby
def initialize(_tag_name, link, _parse_context)
super
stripped_link = ActionController::Base.helpers.strip_tags(link)
the_link = stripped_link.split.first
@embedded_url = KotlinTag.embedded_url(the_link)
end
def render(_context)
ApplicationController.render(
partial: PARTIAL,
locals: {
url: @embedded_url
}
)
end
```
View files can be found in `app/views/liquids`.
Each new liquid tag should be accompanied by instructions in
`app/views/pages/_editor_guide_text.html.erb`.
Liquid Tags should also be accompanied by tests in `spec/liquid_tags` which
confirm expected behavior.
Some Liquid Tags are constructed using HTML and CSS within the app, and some are
constructed by displaying an iframe of an external site.
CSS for Liquid Tags are found in `app/assets/stylesheets/ltags`. Liquid tag
classes should generally be prepended by `ltag__`. e.g. `ltag__tag__content`
etc.
Here is an example of a good Liquid Tag pull request...
https://github.com/forem/forem/pull/3801
### Restricting liquid tags by roles
To only allow users with specific roles to use a liquid tag, you need to define
a `VALID_ROLES` constant on the liquid tag itself. It needs to be an `Array` of
valid roles. For [single resource roles](/admin), it needs to be an `Array` with
the role and the resource. Here's an example:
```ruby
class NewLiquidTag < LiquidTagBase
VALID_ROLES = [
:admin,
[:restricted_liquid_tag, LiquidTags::UserSubscriptionTag]
].freeze
end
```
Here we are saying that the `UserSubscriptionTag` is only usable by users with
the `admin` role or with a role of `:restricted_liquid_tag` and a specified
resource of `LiquidTags::UserSubscriptionTag`.
`LiquidTags::UserSubscriptionTag` is a resource model so we that can play nicely
with the [Rolify][rolify] gem. See [/admin](/admin) for more information.
**REMINDER: if you do not define a `VALID_ROLES` constant, the liquid tag will
be usable by all users by default.**
### Restricting liquid tags by context
Context, in terms of a liquid tag, is _where_ a liquid tag is being used (i.e.
`Article`, `Comment`, etc.). In other words, if you want to make a liquid tag
that can only be used in articles, you need to restrict the liquid tag by
context.
To do this you need to add a `VALID_CONTEXTS` constant on the liquid tag itself.
It needs to be an `Array` of class names that are valid. For example, to
restrict a liquid tag to only be usable in articles you would do:
```ruby
class NewLiquidTag < LiquidTagBase
VALID_CONTEXTS = %w[Article].freeze
end
```
**REMINDER: if you do not define a `VALID_CONTEXTS` constant the liquid tag will
be usable in all contexts by default.**
[rolify]: https://github.com/RolifyCommunity/rolify

View file

@ -1,44 +0,0 @@
---
title: JavaScript and Initializers
---
# JavaScript and Initializers
Forem has two JavaScript codebases.
One is located in the directory `app/assets/javascripts` and contains plain
JavaScript (mostly ES5+) being served using
[Sprockets](https://github.com/rails/sprockets-rails) which packages static
assets.
Webpacker manages the other one,
[which you can read more about in this guide](/frontend/webpacker).
This source code is not transpiled, only packaged and minified, and will be
limited to whatever flavor of JavaScript can run on the user's web browser.
`app/assets/javascripts/application.js` contains the manifest JavaScript file
which is included globally in the primary template,
`app/views/layouts/application.html.erb`.
`application.js` automatically includes all JS files via the statement:
```erb
//= require_tree .
```
One JS file in particular, `app/assets/javascripts/initializePage.js`,
bootstraps the majority of the functionality. You will notice, within this file,
that major sections of the websites are bootstrapped, for example:
```javascript
initializeBaseTracking();
initializeCommentsPage();
initEditorResize();
initLeaveEditorWarning();
initializeArticleReactions();
initNotifications();
initializeSplitTestTracking();
```
All the "initializers" are in `/app/assets/javascripts/initializers`.

View file

@ -1,85 +0,0 @@
---
title: Preact
---
# Preact
[Preact](https://preactjs.com/) is an alternative to React with the same modern
API.
Preact components are packaged using [Webpacker](/frontend/webpacker) and the
Preact code is located in `app/javascript`.
Preact components get loaded via webpacker's helper function
`javascript_packs_with_chunks_tag`.
## PropTypes
Preact supports
[PropTypes](https://reactjs.org/docs/typechecking-with-proptypes.html). When
creating Preact components, please ensure that you have defined your PropTypes.
### Common PropTypes
Using PropTypes can be repetitive. Some duplication is normal, like when a
PropType is a string or a number. But for commonly-used PropTypes, like the user
entity, you can use the provided common PropTypes, located in the
`/app/javascript/common-prop-types`, as shown below.
```javascript
import PropTypes from 'prop-types';
export const userPropTypes = PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
profile_image_url: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
});
```
#### Using Common PropTypes
Common PropTypes are imported just like any other
[JavaScript Module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules).
For example, here are two scenarios where a component needs to use the
`tagPropTypes`.
In the example below, our component `SomeComponentUsingTags` has a `tags` prop,
which is an array of the tag entity. PropTypes have a built-in method called
`arrayOf` that allows you to define a prop as an array of something. In our
case, this is the tag entity, so we can use the `tagPropTypes` PropType.
```jsx
import { h } from 'preact';
import PropTypes from 'prop-types';
import { tagPropTypes } from '../../../components/common-prop-types';
const SomeComponentUsingTags = ({ tags = [] }) => (
<ul>
{tags.map((tag) => (
<li key={tag.id}>{tag.name}</li>
))}
</ul>
);
SomeComponentUsingTags.displayName = 'SomeComponentUsingTags';
SomeComponentUsingTags.propTypes = {
tags: PropTypes.arrayOf(tagPropTypes).isRequired,
};
```
In the following example, the `SomeComponentUsingOneTag` component has a `tag`
prop representing a single tag. In this case, we can just the `tagPropTypes` on
their own to represent the shape of the `tag` prop.
```jsx
import { h } from 'preact';
import { tagPropTypes } from '../../../components/common-prop-types';
const SomeComponentUsingOneTag = ({ tag }) => <li key={tag.id}>{tag.name}</li>;
SomeComponentUsingOneTag.displayName = 'SomeComponentUsingTags';
SomeComponentUsingOneTag.propTypes = {
tag: tagPropTypes.isRequired,
};
```

View file

@ -1,17 +0,0 @@
---
items:
- plain-js.md
- webpacker.md
- preact.md
- instant-click.md
- dynamic-imports.md
- styles.md
- linting-formatting.md
- liquid-tags.md
- debugging.md
- tracking.md
- accessibility.md
- tips.md
---
# Frontend Guide

View file

@ -1,37 +0,0 @@
---
title: Styles
---
# Styles
The majority of the CSS in the application is written in
[SASS](https://sass-lang.com/). There are a few places in the code base that
have style blocks in ERB templates, for inlining critical CSS (good). There are
also some styles that live in ERB templates that are not critical CSS (bad).
That is a bit of refactoring that needs to be done. PRs welcome!
Important files when working with SASS in the project:
- variables:
[/app/assets/stylesheets/variables.scss](https://github.com/forem/forem/blob/main/app/assets/stylesheets/variables.scss)
- mixins:
[/app/assets/stylesheets/\_mixins.scss](https://github.com/forem/forem/blob/main/app/assets/stylesheets/_mixins.scss)
SASS is compiled and served using
[Sprockets](https://github.com/rails/sprockets-rails) which packages static
assets in Rails.
For more about branding, theming or design in general in regards to Forem, refer
to the [Design Guide](/design) documentation.
## Crayons
Crayons is the design system used by Forem. A
[storybook](https://storybook.js.org/) listing the various elements is available
at https://storybook.forem.com/
You can also run it locally with the following command:
```
$ yarn storybook
```

View file

@ -1,46 +0,0 @@
---
title: Tips
---
# Tips
## About query selectors
JavaScript has many different query selectors, some seemingly interchangeable,
for example:
- `document.head` and `document.body`
- `document.getElementById`, `document.getElementsByClassName` and
`document.getElementsByTagName`
- `document.querySelector` and `document.querySelectorAll`
Knowing which to use for optimal performance depends on the situation, but a
good rule of thumb is:
- to access the **head** of the document, use `document.head` over
`document.getElementsByTagName('head')`
- to access the **body** of the document, use `document.body` over
`document.getElementsByTagName('body')`
- to access an element by id use `document.getElementById('id')` over
`document.querySelector('#id')`
- to access one element by class name use
`document.getElementsByClassName('className')[0]` over
`document.querySelector('.className')`
- to access multiple elements by class name use
`document.getElementsByClassName('className')` over
`document.querySelectorAll('.className')`
- to access one element by tag name use
`document.getElementsByTagName('tagName')[0]` over
`document.querySelector('tagName')`
- to access multiple elements by tag name use
`document.getElementsByTagName('tagName')` over
`document.querySelectorAll('tagName')`
In most cases `querySelector` and `querySelectorAll` should be used only on
selectors more sophisticated than a simple id, class or tag name.
### Resources
- [Forem PR 6380](https://github.com/forem/forem/issues/6380#issuecomment-592989438)
- [Why is getElementsByTagName() faster than querySelectorAll()?](https://humanwhocodes.com/blog/2010/09/28/why-is-getelementsbytagname-faster-that-queryselectorall/)
- [What is the difference between querySelectorAll and getElementsByTagName?](https://stackoverflow.com/a/30921553/4186181)

View file

@ -1,29 +0,0 @@
---
title: Tracking
---
## Ahoy.js
For first-party analytics, we use the
[`ahoy.js` library](https://github.com/ankane/ahoy.js), which tracks visits and
events. This library works in conjunction with the `ahoy_matey` gem, which is
documented in our [backend tracking guide](/backend/tracking).
### Configuration
The configuration for `ahoy.js` lives in `app/assets/javascripts/base.js.erb`.
Since we do not track user cookies on the backend, we have configured
`ahoy.js`'s defaults to match that on the frontend.
### Events
In order to track an event, use the `ahoy.track` function:
```javascript
ahoy.track(name, properties);
```
This function will send a `POST` request to the `/ahoy/events` endpoint on our
backend with the `name` and `properties` of the event. The backend endpoint will
also create a corresponding `Ahoy::Visit` for the event if one does not exist
already.

View file

@ -1,56 +0,0 @@
---
title: Webpacker
---
# Webpacker
Forem has two JavaScript codebases.
One contains plain JavaScript,
[which you can read more about in this guide](/frontend/plain-js).
The other one is managed by [Webpacker](https://github.com/rails/webpacker), and
it's located inside `/app/javascript`, written using ES6+.
Currently, it's mainly used for Preact components, served via `webpack` which is
integrated into the Rails app using `Webpacker`.
There is a packs directory `/app/javascript/packs` where you can create new
"pack" files. Pack files are initializers for Webpacker.
Since Forem is not a Single Page Application (SPA), Preact components are
mounted as needed by including the pack file in the view files.
For example:
```erb
<%= javascript_packs_with_chunks_tag "webShare", defer: true %>
```
The include statement corresponds to the pack `app/javascript/packs/webShare.js`
If you have more than one webpacker pack on the page, you need to include it in
the same `javascript_packs_with_chunks_tag` call. The reason being is it avoids
loading split chunks multiple times.
```erb
<%= javascript_packs_with_chunks_tag "webShare", "someOtherPack", defer: true %>
```
## Webpack aliases
The project uses
[webpack aliases](https://webpack.js.org/configuration/resolve/#resolvealias).
The aliases used in the project can be found under `alias` in
https://github.com/forem/forem/blob/master/config/webpack/environment.js
## Additional Resources
For more information in regards to `javascript_packs_with_chunks_tag`, see
https://github.com/rails/webpacker/blob/main/lib/webpacker/helper.rb
Aside from the Webpacker repository, see also Ross Kaffenberger's
[visual guide to Webpacker](https://rossta.net/blog/visual-guide-to-webpacker.html).
If you're interested in bundles sizes and what's contained within them for a
production build, run `bin/bundleAnalyzer` from the command line.

View file

@ -1,28 +0,0 @@
---
title: Creating a Branch
---
# Creating a feature/bug branch
When you are working on a bug, feature, or improvement, you will need to create
a branch.
Branches names should be prefixed with your own GitHub username. If they have an
associated issue, the issue ID should be added as a suffix. For example:
```shell
git checkout -b USERNAME/that-new-feature-1234
```
or
```shell
git checkout -b USERNAME/fixing-that-bug-1234
```
where `USERNAME` should be replaced by your username on GitHub and `1234` is the
ID of the issue tied to your pull request. If there is no related issue, you can
leave the number out.
NOTE: It is ok if your branch name shows up as
`USERNAME:USERNAME/fixing-that-bug-1234` when you create the pull request

View file

@ -1,40 +0,0 @@
---
title: Committing
---
# Committing and pre-commit hooks
## Commit messages
We encourage people to write
[meaningful commit messages](https://chris.beams.io/posts/git-commit/).
## Style guide
This project follows
[thoughtbot's Ruby Style Guide](https://github.com/thoughtbot/guides/blob/main/ruby/.rubocop.yml),
using [Rubocop](https://github.com/bbatsov/rubocop) along with
[Rubocop-Rspec](https://github.com/backus/rubocop-rspec) as the code analyzer.
If you have Rubocop installed with your text editor of choice, you should be up
and running.
For the frontend, [ESLint](https://eslint.org) and
[prettier](https://github.com/prettier/prettier) are used. ESLint's recommended
rules along with Preact's recommended rules are used for code-quality.
Formatting is handled by prettier. If you have ESLint installed with your text
editor of choice, you should be up and running.
## Husky hooks
When commits are made, a git precommit hook runs via
[husky](https://github.com/typicode/husky) and
[lint-staged](https://github.com/okonet/lint-staged). ESLint, prettier, and
Rubocop will run on your code before it's committed. If there are linting errors
that can't be automatically fixed, the commit will not happen. You will need to
fix the issue manually then attempt to commit again.
Note: if you've already installed the [husky](https://github.com/typicode/husky)
package at least once (used for pre-commit npm script), you will need to run
`yarn --force` or `npm install --no-cache`. For some reason, the post-install
script of husky does not run when the package is pulled from yarn or npm's
cache. This is not husky specific, but rather a cached package issue.

View file

@ -1,39 +0,0 @@
---
title: Configuring Environment Variables
---
# Configuring environment variables and secret keys
Take a look at `.env_sample`. This file lists all the `ENV` variables we use and
provides a fake default for any missing keys.
The [backend guide][backend_guide] will show you how to get free API keys for
additional services that may be required to run certain parts of the app.
To set up keys for your local instance of Forem, you'll need to create an `.env`
file. You can do this by copying the file called `.env_sample` in the app's main
directory:
```shell
cp .env_sample .env
```
Then, add each key you need to the `.env` file. For example, if you're setting
up Cloudinary:
```shell
export CLOUDINARY_API_KEY="SOME_REAL_SECURE_KEY_HERE"
export CLOUDINARY_API_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
export CLOUDINARY_CLOUD_NAME="A_CLOUDINARY_NAME"
```
(Don't worry, your `.env` file is ignored by git)
If you are missing `ENV` variables when you boot your application you will see a
warning message in your logs when you try to access that variable
`Unset ENV variable: xyz`.
Only certain features require "real" keys, so you may be able to add them as you
work on different areas of the application.
[backend_guide]: /backend

View file

@ -1,68 +0,0 @@
---
title: Preparing the Database
---
# Preparing the database
The next step is to create and prepare the database. Because Forem is a Rails
application, we have built-in tools to help us.
We can use Rails to create our database, load the schema, and add some seed
data:
```shell
rails db:setup
```
Note: If you've already run `bin/setup`, this will have already been done for
you.
`db:setup` actually runs the following rake commands in order so alternatively,
you could run each of these to produce the same result:
```shell
rails db:create
rails db:schema:load
rails db:seed
```
## Seed Data
By default, the amount of articles and users generated is quite tiny so that
contributors experience a quick installation. If you require more data for your
local installation, you can tune amount of data generated with the environment
variable `SEEDS_MULTIPLIER`.
This variable, which defaults to `1`, allows the developer to increase the size
of their local DB. For example:
```shell
SEEDS_MULTIPLIER=2 rails db:setup
```
will result in creating double the default amount of items in the database.
It's currently used only for `articles` and `users`.
It can also be used for `rails db:seed` and `rails db:reset`.
## Default Admin User
Seed data creates a handful of regular users, and a single admin user that can
be used to log into the application with the Email login option:
```
email: admin@forem.local
password: password
```
### Other seed modes
To put your local forem into "starter mode", as it would be for a new creator,
use `MODE=STARTER` i.e...
```shell
MODE=STARTER rails db:setup
```
This mode skips creation of all sample data.

View file

@ -1,29 +0,0 @@
---
title: Forking the Repo
---
# Forking the repository
The first step in contributing to any Open Source project on GitHub is
[forking][forking] so that you can create [Pull Requests][pull_requests]. If
you're not familiar with the idea of forking a repository, [GitHub has a super
simple tutorial][forking_tutorial]!
If you're ready to get started, [fork Forem's repository][fork_dev]!
Once you've got a fork on GitHub, you can clone your fork to run the project
locally:
```shell
git clone https://github.com/<your-username>/forem.git
```
As time goes on, you may need to refresh your fork. If you have trouble with
that, you can read about [keeping a fork caught up on Forem][refresh_your_fork]!
[forking]: https://help.github.com/en/articles/fork-a-repo
[fork_dev]: https://github.com/forem/forem/fork
[forking_tutorial]: https://guides.github.com/activities/forking/
[pull_requests]: https://help.github.com/en/articles/about-pull-requests
[refresh_your_fork]:
https://dev.to/jacobherrington/a-fool-proof-way-to-keep-your-fork-caught-up-in-git-2e2e

View file

@ -1,26 +0,0 @@
---
title: Preparing a Pull Request
---
# Preparing a pull request
- Try to keep the pull requests small. A pull request should try its very best
to address only a single concern.
- If you plan to do further work after the PR has been submitted, please use the
[Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/)
feature.
- Make sure all tests pass and add additional tests for the code you submit.
[More info here](https://docs.forem.com/tests/).
- Document your reasoning behind the changes. Explain why you wrote the code in
the way you did. The code should explain what it does.
- If there's an existing issue related to the pull request, reference to it by
adding something like `References/Closes/Fixes/Resolves #305`, where 305 is
the issue number.
[More info here](https://github.com/blog/1506-closing-issues-via-pull-requests).
- Please fill out the PR Template when making a PR.
- All commits in a pull request will be squashed when merged, but when your PR
is approved and passes our CI, it will eventually be live on production!
If the pull request affects the public API in any way, a post on Forem from the
Forem Team account should accompany it. This is the duty of the core team to
carry out.

View file

@ -1,16 +0,0 @@
---
title: Getting Started
items:
- forking.md
- config-env.md
- db.md
- start-app.md
- workflow.md
- branching.md
- committing.md
- pull-request.md
- syncing.md
- architecture.md
---
# Getting Started!

View file

@ -1,131 +0,0 @@
---
title: Starting the Application
---
We're a Rails app, and we use [Webpacker][webpacker] to manage some of our
JavaScript.
# Starting the application
Start the application, Webpack, and our job runner [Sidekiq][sidekiq] by
running:
```shell
bin/startup
```
(This just runs `foreman start -f Procfile.dev`, for notes on how to install
Foreman, please see [Other Tools](/installation/others/))
Then point your browser to http://localhost:3000/ to view the site. To log in
use the admin account created by default (see
[Database](/getting-started/db/#default-admin-user))
If you run into issues while trying to run `bin/setup` and the error message
isn't helpful, try running `bin/rails s -p 3000`. For example, you may need to
`yarn install` before starting the app.
If Sidekiq is producing errors similar to
`No such file or directory - [SOME FILE]`, you may need to start Sidekiq by
itself once to help it initialize itself fully. You can use the command
`bundle exec sidekiq` to do this.
If you're working on Forem regularly, you can use `alias start="bin/startup"` to
make this even easier. 😊
If you're using **`pry`** for debugging in Rails, note that using `foreman` and
`pry` together works, but it's not as clean as `bin/rails server`.
Here are some singleton commands you may need, usually in a separate
instance/tab of your shell.
- Running the job Sidekiq server (if using `bin/rails server`) -- this is mostly
for notifications and emails: **`bundle exec sidekiq`**
Current gotchas: potential environment issues with external services need to be
worked out.
# Starting the application (advanced)
To have additional control in your local environment, you might prefer using an
advanced setup to start the application by using [Overmind][overmind].
The prerequisite is to install [Overmind][overmind], a process manager which
takes advantage of [tmux][tmux].
This will allow you to launch all your app's processes in the same terminal,
navigate the logs of each service separately, restart each service separately
and have a better debugging experience.
After installing [Overmind][overmind], launch the application:
```shell
overmind s -f Procfile.dev
```
## Debugging the Rails application
[Overmind][overmind] lets you easily step through the Rails application in a
debugging session.
Using the `pry` gem, you can add a `binding.pry` to set a breakpoint in the
method you're trying to debug; the application will halt its execution there.
You can then connect to the web server by opening a separate terminal window and
typing:
```shell
overmind c web
```
This will open up a [tmux][tmux] window pane at the debugging statement
position, which will look something like this:
```ruby
pry(#<Admin::OverviewController>)> whereami
From: /forem/app/controllers/admin/overview_controller.rb:8 Admin::OverviewController#index:
5: def index
6: a = "Hello debugger"
7: binding.pry
=> 8: end
```
## Inspecting the logs of each service
Overmind launches the various services required for our local setup: `web` (the
Rails web server), `webpacker` (the server managing JavaScript) and `sidekiq`
(the server managing the asynchronous queue).
If, for example, you want to inspect just the Sidekiq logs, you can open a
separate terminal window to look at those logs specifically:
```shell
overmind c sidekiq
```
This will open a `tmux` console, which will allow you to browse _only_ the
Sidekiq logs.
There are also some handy `tmux` shortcuts that you may find useful.
- The shortcut `C-b [` (_Control-b-open square bracket_) activates "scroll
mode", which allows you to use the arrows up and down and inspect the logs.
- The shortcut `q` deactivates "scroll mode".
Please refer to [tmux][tmux] documentation for more information around `tmux`
configuration and for additional shortcuts.
## Resources
Other than the official [Overmind][overmind] and [tmux][tmux] documentation, you
may find the following resources useful:
- [Rails quick tips #6: tmux, tmuxinator and Overmind](https://dev.to/citizen428/rails-quick-tips-6-tmux-tmuxinator-and-overmind-4850)
- [Give Your Terminal Super Powers: tmux Cheatsheet!](https://dev.to/jacobherrington/give-your-terminal-super-powers-tmux-cheatsheet-1p6p)
- [Introducing Overmind and Hivemind](https://evilmartians.com/chronicles/introducing-overmind-and-hivemind)
[sidekiq]: https://github.com/mperham/sidekiq
[webpacker]: https://github.com/rails/webpacker
[overmind]: https://github.com/DarthSim/overmind
[tmux]: https://github.com/tmux/tmux/wiki

View file

@ -1,89 +0,0 @@
---
title: Keeping Your Fork In Sync
---
# Keeping your fork in sync
Now that you have a fork of Forem's source code, there is work you will need to
do to keep it updated.
## Setup your upstream
Inside your Forem directory, add a remote to the official Forem repo:
```shell
git remote add upstream https://github.com/forem/forem.git
```
## Rebasing from upstream
Do this prior to creating each branch for a PR:
Make sure you are on the main branch:
```shell
$ git status
On branch main
Your branch is up-to-date with 'origin/main'.
```
If you aren't on `main`, finish your work and checkout the `main` branch:
```shell
git checkout main
```
Do a pull with rebase against `upstream`:
```shell
git pull --rebase upstream main
```
This will pull down all of the changes to the official `main` branch, without
making an additional commit in your local repo.
(Optional) Force push your updated `main` branch to your GitHub fork
```shell
git push origin main --force
```
This will overwrite the `main` branch of your fork.
## Keeping your branch up to date
Sometimes, your forked branch may get out of date. To get it up to date it,
carry out the following steps:
Rebase from upstream once again:
```shell
git checkout main
git pull --rebase upstream main
```
Checkout your feature branch locally and merge main back into your branch:
```shell
git checkout <feature-branch-name>
git merge main
```
Merge any conflicts in editor if necessary:
```shell
git commit -m "Fix merge conflicts"
```
Push the changes back to your origin feature branch:
```shell
git push origin <feature-branch-name>
```
After you've fetched new commits from upstream, run `./bin/setup`, and it will
install new gems, npm packages, update database, and restart Rails server.
## Additional resources
- [Syncing a fork](https://help.github.com/articles/syncing-a-fork/)

View file

@ -1,55 +0,0 @@
---
title: Suggested Workflow
---
# Suggested workflow
We use [Spring](https://github.com/rails/spring), and it is already included in
the project.
1. Use the provided bin stubs to automatically start Spring, i.e.
`bin/rails server`, `bin/rspec spec/models/`, `bin/rails db:migrate`.
1. If Spring isn't picking up on new changes, use `spring stop`. For example,
Spring should always be restarted if there's a change in the environment
keys.
1. Check Spring's status whenever with `spring status`.
Caveat: `bin/rspec` is not equipped with Spring because it affects Simplecov's
result. Instead, use `bin/spring rspec`.
## Synchronizing a fork with upstream / integrate latest changes
When changes in the upstream repository happen, the fork does not get those
automatically and this is by design. To integrate the changes to the upstream
repo that were committed since you cloned your fork or synced the last time, use
the following script: `./scripts/sync_fork.sh` This will fetch the changes and
merge them into your current workspace.
Use this:
- to get commits from upstream main into your branch
- to sync with latest changes from upstream main before continuing with a new
feature on your current branch
After you've fetched new commits from upstream, run `./bin/setup`, and it will
install new gems, npm packages, update database, and restart Rails server.
Start the app by running `./bin/startup`, if it's not already running.
## Start over / discard all your changes
Sometimes it is neccesarry to start over from the beginning or reset the current
workspace to the state of the upstream branch. Use the helper
`./scripts/clean_fork.sh` to set your fork to the exact same state as the
upstream main branch.
Use this:
- before working on a new feature
- before creating a new branch to make sure to have all the latest changes in
your fork also.
After you've done that, run `./bin/setup`, and it will update gems, npm
packages, update database, and restart Rails server.
Start the app by running `./bin/startup`, if it's not already running.

View file

@ -1,121 +0,0 @@
---
title: Containers
---
# Installing Forem using Containers
If you encounter any errors with our Container setup, please kindly
[report any issues](https://github.com/forem/forem/issues/new/choose)!
## Installing prerequisites
_These prerequisites assume you're working on an operating system supported by
Docker or Podman._
### Choosing a Container Engine
A container engine is software that runs and manages containers on a computer.
One of the most widely known Container Engines is Docker, but there are many
other Container Engines available, such as [Podman](https://podman.io/),
[CRI-O](https://cri-o.io/), and
[LXD](https://linuxcontainers.org/lxd/introduction/).
Forem supports two Container Engines: Docker and Podman.
### Docker
Forem can be setup with Docker and Docker Compose on macOS or Linux systems.
Docker is available for many different operating systems. You may use Docker as
your Container Engine on both macOS and Linux workstations. As of right now
Docker is the only Container Engine for macOS and we recommend you follow the
[Docker Desktop on Mac](https://docs.docker.com/docker-for-mac/install/),
install instructions to get Docker and Docker Compose installed.
Docker also works well on Linux distributions that have not moved to cgroup v2.
You can install it by following their
[Installation per distro](https://docs.docker.com/engine/install/) to get Docker
and you can install Docker Compose by following these
[instructions](https://docs.docker.com/compose/install/).
### Podman
Forem can be setup with Podman and Podman Compose on Linux systems.
[Podman](https://podman.io/) is an FOSS project that provides a Container Engine
that is daemonless which only runs on Linux systems. It can be run as the root
user or as a non-privileged user. It also provides a Docker-compatible command
line interface. Podman is available on many different Linux distributions and it
can be installed by following these
[instructions](https://podman.io/getting-started/installation).
[Podman Compose](https://github.com/containers/podman-compose) is a an early
project under development that is implementing docker-compose like experience
with Podman. You can install it by following these
[instructions](https://github.com/containers/podman-compose#installation).
## Setting up Forem
1. Fork Forem's repository, e.g. <https://github.com/forem/forem/fork>
1. Clone your forked repository, eg.
`git clone https://github.com/<your-username>/forem.git`
1. Set up your environment variables/secrets
1. Create `.env` by copying from the provided template`.env_sample` (i.e.
with bash: `cp .env_sample .env`).
- `.env` is a personal file that is **ignored in git**.
- `.env` lists all the `ENV` variables we use and provides a fake
default for any missing keys.
2. For any key that you wish to enter/replace:
- You do not need "real" keys for basic development. Some features
require certain keys, so you may be able to add them as you go.
- The [backend guide](/backend) will show you how to get free API keys
for additional services that may be required to run certain parts of
the app.
- Obtain the development variable and apply the key you wish to
enter/replace. i.e.:
```shell
export CLOUDINARY_API_KEY="SOME_REAL_SECURE_KEY_HERE"
export CLOUDINARY_API_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
export CLOUDINARY_CLOUD_NAME="A_CLOUDINARY_NAME"
```
## Running Forem with Docker via docker-compose
1. Run `bin/container-setup`
2. That's it! Navigate to <http://localhost:3000>
The script executes the following steps:
1. `docker-compose build`
2. `docker-compose up`
## Running Forem with Podman via podman-compose
1. Run `bin/container-setup`
2. That's it! Navigate to <http://localhost:3000>
The script executes the following steps:
1. `podman-compose build`
2. `podman-compose up`
## Known Problems & Solutions
### Docker on Mac
- In case `rails server` starts with the following message:
```shell
Data update scripts need to be run before you can start the application. Please run rails data_updates:run (RuntimeError)
```
run the following command:
```shell
docker-compose run rails rails data_updates:run
```

View file

@ -1,18 +0,0 @@
---
title: GitPod
---
# GitPod
If you prefer working on a cloud IDE, you can spin up your own instance of Forem
in the cloud, using [GitPod](https://gitpod.io/#https://github.com/forem/forem).
[![Open in
Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/forem/forem)
Give it a couple of minutes to prepare the workspace. Wait for 'Awaiting port 3000... ok' message in the terminal.
You can now code, review, or just try out the project. Once you are done, simply close the tab.
You can find more information about this,
[in a Forem's article](https://dev.to/ben/spin-up-a-local-instance-of-dev-in-the-cloud-with-gitpod-it-s-incredibly-simple-pij).

View file

@ -1,75 +0,0 @@
---
title: Imgproxy
---
# Imgproxy
Imgproxy is a standalone server for resizing images. It is optional and you do
not need it to start Forem locally. We are currently only using it in Forem
Cloud.
## Installation
- MacOS: install via homebrew with `brew install imgproxy`.
- Windows: please install
[via docker](https://docs.imgproxy.net/#/installation?id=docker).
- Linux: you can either use
[docker installation](https://docs.imgproxy.net/#/installation?id=docker) or
[build from source](https://docs.imgproxy.net/#/installation?id=from-the-source).
For more options not covered here, please take a look at the
[official installation documentation](https://docs.imgproxy.net/#/installation).
## Usage
1. Generate a key/salt pair by running the following in your terminal twice.
Copy those values to your `.env` in the next step
```
echo $(xxd -g 2 -l 64 -p /dev/random | tr -d '\n')
```
1. In your `.env`, add the following.
```
export IMGPROXY_ENDPOINT='http://localhost:8080'
export IMGPROXY_KEY='1b1c9aae804e070b0864f2547fba7ce8ff31bf7..........'
export IMGPROXY_SALT='8c6d449d4fc2cada5bab538826cae709d2ade9f.........'
```
1. Start the Forem app server normally.
1. Start Imgproxy in a terminal with the key and salt.
```
# If you installed via homebrew or using the binary.
> IMGPROXY_KEY='your key' IMGPROXY_SALT='your salt' imgproxy
# If you are using Docker or Podman. The commands are identical for both
> docker run -p 8080:8080 \
-e IMGPROXY_KEY="your key" \
-e IMGPROXY_SALT="your salt" \
-it darthsim/imgproxy
```
1. That's it :)
You should verify it's working by starting the Forem app locally and see that
each image is loaded properly, or run the following command while the Forem app
is running:
```
> curl -I http://localhost:8080/unsafe/aHR0cDovL2xvY2Fs/aG9zdDozMDAwL2Fz/c2V0cy8xLnBuZw
HTTP/1.1 200 OK
Server: imgproxy
X-Request-Id: GYvCGXb98JUwL3ujwpjzh
Date: Tue, 27 Oct 2020 16:11:37 GMT
```
## Sidenote
- Because Imgproxy is a standalone server of its own, all image URLs given to it
need to be absolute URLs.
- When working with Docker or Podman on Linux, provide the host network option
(`--network="host"`) so Imgproxy can properly access the localhost.

View file

@ -1,115 +0,0 @@
---
title: Linux
---
# Installing Forem on Linux
## Installing prerequisites
_These prerequisites assume you're working on a Linux-based operating system,
but they have only been tested on Ubuntu 18.04._
### Ruby
1. If you don't already have a Ruby version manager, we highly recommend
[rbenv](https://github.com/rbenv/rbenv). Please follow their
[installation guide](https://github.com/rbenv/rbenv#installation).
1. With the Ruby version manager, install the Ruby version listed on our badge.
(ie with rbenv: `rbenv install $(cat .ruby-version)`)
For very detailed rbenv installation directions on several distros, please visit
[DigitalOcean's guide](https://www.digitalocean.com/community/tutorials/how-to-install-ruby-on-rails-with-rbenv-on-ubuntu-18-04).
### Yarn
There are two ways to install Yarn.
- Yarn's official
[installation guide](https://yarnpkg.com/en/docs/install#debian-stable)
(recommended).
- [DigitalOcean's detailed tutorial](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-18-04)
describes how to install
[Node version Manager](https://github.com/creationix/nvm). By installing NVM
you can select a Node version (we recommend either LTS or current); the guide
will also explain how to install NPM. This way you'll have Node, NPM, and then
you can run `npm install -g yarn` to install Yarn.
### PostgreSQL
Forem requires PostgreSQL version 11 or higher.
1. Run
`sudo apt update && sudo apt install postgresql postgresql-contrib libpq-dev`.
1. To test the installation you can run `sudo -u postgres psql` which should
open a PostgreSQL prompt. Exit the prompt by running `\q` then run
`sudo -u postgres createuser -s $YOUR_USERNAME` where `$YOUR_USERNAME` is the
username you are currently logged in as.
There are more than one ways to setup PostgreSQL. For additional configuration,
check out our [PostgreSQL setup guide](/installation/postgresql) or the official
[PostgreSQL](https://www.postgresql.org/) site for further information.
### ImageMagick
Forem uses [ImageMagick](https://imagemagick.org/) to manipulate images on
upload.
Please refer to ImageMagick's
[instructions](https://imagemagick.org/script/download.php) on how to install
it.
### Redis
Forem requires Redis version 6.0 or higher.
We recommend following Digital Ocean's extensive guides
[How to Install and Secure Redis](https://www.digitalocean.com/community/tutorial_collections/how-to-install-and-secure-redis)
to setup Redis.
## Installing Forem
1. Fork Forem's repository, e.g. <https://github.com/forem/forem/fork>
1. Clone your forked repository, e.g.
`git clone https://github.com/<your-username>/forem.git`
1. Install bundler with `gem install bundler`
1. Set up your environment variables/secrets
- Take a look at `.env_sample`. This file lists all the `ENV` variables we
use and provides a fake default for any missing keys.
- If you use a remote computer as dev env, you need to set `APP_DOMAIN`
variable to the remote computer's domain name.
- The [backend guide](/backend) will show you how to get free API keys for
additional services that may be required to run certain parts of the app.
- For any key that you wish to enter/replace:
1. Create `.env` by copying from the provided template (i.e. with bash:
`cp .env_sample .env`). This is a personal file that is ignored in git.
1. Obtain the development variable and apply the key you wish to
enter/replace. i.e.:
```shell
export CLOUDINARY_API_KEY="SOME_REAL_SECURE_KEY_HERE"
export CLOUDINARY_API_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
export CLOUDINARY_CLOUD_NAME="A_CLOUDINARY_NAME"
```
- You do not need "real" keys for basic development. Some features require
certain keys, so you may be able to add them as you go.
1. Run `bin/setup`
### Possible error messages
While installing, you might run into an error due to the `pg` gem requiring
PostgreSQL libraries. If so, please run `sudo apt-get install libpq-dev` before
retrying.
While installing, you might run into an error due to the `sass-rails` gem
requiring `sassc`, which requires the `g++` compiler. If so, please run
`sudo apt-get install g++` before retrying.
While installing, if you didn't install `node` or `nvm` manually, you might run
into an error due to an older system node version being present, which can cause
issues while `yarn` is installing packages. If so, you'll need to
[install `nvm`](https://github.com/nvm-sh/nvm#installation-and-update) and then
run `nvm install node` to get the most recent node version before retrying.

View file

@ -1,209 +0,0 @@
---
title: macOS
---
# Installing Forem on macOS
## Installing prerequisites
### Ruby
1. **Note:** MacOS ships with a version of Ruby, needed for various operating
systems. To avoid causing an issue with your operating system you should use
a version manager for Ruby.
If you don't already have a Ruby version manager, we highly recommend
[rbenv](https://github.com/rbenv/rbenv). This will allow you to have
different versions running on a per project basis. The MacOS system version
of Ruby will stay intact while giving you the ability to use the version
needed for this Forem project. Please follow their
[installation guide](https://github.com/rbenv/rbenv#installation).
2. With the Ruby version manager, install the Ruby version listed on our badge.
(i.e. with rbenv: `rbenv install $(cat .ruby-version)`)
**Note:** The repository must be forked and cloned before running the
`rbenv install $(cat .ruby-version)` command.
### Yarn
Please refer to their [installation guide](https://yarnpkg.com/en/docs/install).
### PostgreSQL
Forem requires PostgreSQL version 11 or higher to run.
The easiest way to get started is to use
[Postgres.app](https://postgresapp.com/). Alternatively, check out the official
[PostgreSQL](https://www.postgresql.org/) site for more installation options.
For additional configuration options, check our
[PostgreSQL setup guide](/installation/postgresql).
### ImageMagick
Forem uses [ImageMagick](https://imagemagick.org/) to manipulate images on
upload.
You can install ImageMagick with `brew install imagemagick`.
### Redis
Forem requires Redis version 6.0 or higher to run.
We recommend using [Homebrew](https://brew.sh):
```shell
brew install redis
```
you can follow the post installation instructions, we recommend using
`brew services` to start Redis in the background:
```shell
brew services start redis
```
You can test if it's up and running by issuing the following command:
```shell
redis-cli ping
```
## Installing Forem
1. Fork Forem's repository, e.g. <https://github.com/forem/forem/fork>
2. Clone your forked repository in one of two ways:
- e.g. with HTTPS: `git clone https://github.com/<your-username>/forem.git`
- e.g. with SSH: `git clone git@github.com:<your-username>/forem.git`
3. Install bundler with `gem install bundler`
4. Set up your environment variables/secrets
- Take a look at `.env_sample` to see all the `ENV` variables we use and the
fake default provided for any missing keys.
- If you use a remote computer as dev env, you need to set `APP_DOMAIN`
variable to the remote computer's domain name.
- The [backend guide](/backend) will show you how to get free API keys for
additional services that may be required to run certain parts of the app.
- For any key that you wish to enter/replace, follow the steps below.
1. Create `.env` by copying from the provided template (i.e. with bash:
`cp .env_sample .env`). This is a personal file that is ignored in git.
2. Obtain the development variable and apply the key you wish to
enter/replace. i.e.:
```shell
export CLOUDINARY_API_KEY="SOME_REAL_SECURE_KEY_HERE"
export CLOUDINARY_API_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
export CLOUDINARY_CLOUD_NAME="A_CLOUDINARY_NAME"
```
- You do not need "real" keys for basic development. Some features require
certain keys, so you may be able to add them as you go.
5. Run `bin/setup`
### Possible error messages
**Error:** `rbenv install hangs at ruby-build: using readline from homebrew`
**_Solution:_**
[Stackoverflow answer](https://stackoverflow.com/questions/63599818/rbenv-install-hangs-at-ruby-build-using-readline-from-homebrew)
`RUBY_CONFIGURE_OPTS=--with-readline-dir="$(brew --prefix readline)" rbenv install 2.0.0`
**Error:**
`__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called`
**_Solution:_** Run the command `export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES`
(or `set -x OBJC_DISABLE_INITIALIZE_FORK_SAFETY YES` in fish shell)
---
**Error:** `User does not have CONNECT privilege.`
**_Solution:_** Complete the steps outlined in the
[PostgreSQL setup guide](/installation/postgresql).
---
**Error:**
`rbenv: version '<version number>' is not installed (set by /Path/To/Local/Repository/.ruby-version)`
**_Solution:_** Run the command `rbenv install <version number>`
---
**Error:** `ruby-build: definition not found: <version number>` when `rbenv` was
installed via `brew`.
```shell
ruby-build: definition not found: <version number>
See all available versions with `rbenv install --list`.
If the version you need is missing, try upgrading ruby-build:
```
**_Solution:_** Run the following to update `ruby-build`,
`brew update && brew upgrade ruby-build`. After that, rerun
`rbenv install <version number>` and that version will get installed.
---
**Error:**
```shell
== Preparing database ==
Sorry, you can't use byebug without Readline. To solve this, you need to
rebuild Ruby with Readline support. If using Ubuntu, try `sudo apt-get
install libreadline-dev` and then reinstall your Ruby.
rails aborted!
LoadError: dlopen(/Users/<username>/.rbenv/versions/2.6.5/lib/ruby/2.6.0/x86_64-darwin18/readline.bundle, 9): Library not loaded: /usr/local/opt/readline/lib/libreadline.<some version number>.dylib
```
**_Solution:_** Run
`ln -s /usr/local/opt/readline/lib/libreadline.dylib /usr/local/opt/readline/lib/libreadline.<some version number>.dylib`
from the command line then run `bin/setup` again. You may have a different
version of libreadline, so replace `<some version number>` with the version that
errored.
---
**Error:**
```shell
PG::Error: ERROR: invalid value for parameter "TimeZone": "UTC"
: SET time zone 'UTC'
```
**_Solution:_** Restart your Postgres.app, or, if you installed PostgreSQL with
Homebrew, restart with:
```shell
brew services restart postgresql
```
If that doesn't work, reboot your Mac.
---
**Error:**
```shell
ERROR: Error installing pg:
ERROR: Failed to build gem native extension.
[...]
Can't find the 'libpq-fe.h header
*** extconf.rb failed ***
```
**_Solution:_** You may encounter this when installing PostgreSQL with the
Postgres.app. Try restarting the app and reinitializing the database. If that
doesn't work, install PostgreSQL with Homebrew instead:
`brew install postgresql`
---
> If you encountered any errors that you subsequently resolved, **please
> consider updating this section** with your errors and their solutions.

View file

@ -1,23 +0,0 @@
---
title: Other Tools
---
# Miscellaneous
## Foreman
We use [Foreman](https://github.com/ddollar/foreman) to manage our application
through `Procfile` and `Procfile.dev`. As the
[documentation](https://github.com/ddollar/foreman/blob/master/README.md) points
out,
> Ruby users should take care _not_ to install foreman in their project's
> `Gemfile`. See this
> [wiki article](https://github.com/ddollar/foreman/wiki/Don't-Bundle-Foreman)
> for more details.
Instead install Foreman globally with the following command:
```sh
$ gem install foreman
```

View file

@ -1,71 +0,0 @@
---
title: PostgreSQL
---
# Setup your application with PostgreSQL
Follow the instructions in the installation guide below that corresponds to your
operating system:
1. macOS
- [Postgres.app](https://postgresapp.com/): PostgreSQL installation as a Mac
app
- [Homebrew](https://brew.sh/): if you use Homebrew you can easily install
PostgreSQL with `brew install postgresql`
1. Linux (Ubuntu)
- [Ubuntu `14.04`](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-14-04)
- [Ubuntu `16.04 and higher`](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-16-04)
1. [Windows](https://www.postgresql.org/download/windows/)
_You can find all installation options for a variety of operating systems
[on the official PostgreSQL download page](https://www.postgresql.org/download/)_.
## Configuration
By default, the application is configured to connect to a local database named
`PracticalDeveloper_development`. If you need to specify a username and a
password, you can go about it by using the environment variable `DATABASE_URL`
with a connection string.
The
[official Rails guides](https://guides.rubyonrails.org/configuring.html#connection-preference)
go into depth on how Rails merges the existing `database.yml` with the
connection string.
### Setup `DATABASE_URL` in .env
1. Open your `.env`
1. Add the following:
```shell
export DATABASE_URL=postgresql://USERNAME:PASSWORD@localhost
# Optional: If your test database is in a different url, be sure to set this.
export DATABASE_URL_TEST=postgresql://USERNAME:PASSWORD@localhost
```
1. Replace `USERNAME` with your database username, `PASSWORD` with your database
password.
You can find more details on connection strings in
[PostgreSQL's own documentation](https://www.postgresql.org/docs/10/static/libpq-connect.html#LIBPQ-CONNSTRING).
NOTE: due to how Rails merges `database.yml` and `DATABASE_URL` it's recommended
not to add the database name in the connection string. This will default to your
development database name also during tests, which will effectively empty the
development DB each time tests are run.
## Connection Pooling
We use [PgBouncer](http://www.pgbouncer.org) to manage connection pooling.
Database pooling creates a shared pool of connections to our database, rather
than creating new connection each time. PgBouncer is a wrapper around our
database connection and ensures that we only have a finite set of "cached"
connections to the database. This means that our app doesn't need to actually
connect to the database, it only needs to connect to the pool of connections.
The number of connections to the database (the connection limit) is dependent on
our
[Heroku Postgres plan](https://devcenter.heroku.com/articles/heroku-postgres-plans).
PgBouncer ensures that we do not exceed our plan's connection limit.

View file

@ -1,45 +0,0 @@
---
items:
- mac.md
- windows.md
- linux.md
- containers.md
- gitpod.md
- postgresql.md
- imgproxy.md
- vault.md
- others.md
---
# Installation Guide
There are many different ways you can install the **development environment** so
you can help contribute code to Forem.
## Installing locally
You can install Forem to your local machine and we have instructions for
[Mac](/installation/mac), [Windows](/installation/windows) and
[Linux](/installation/linux).
## Running with containers
Installing to your local machine can be troublesome for many reasons such as a
conflicting database and runtime versions.
Another way you can get a development environment up and running is with
containers. They will set up everything you need in an isolated environment, and
you need not worry about the details of setting everything up locally. We have
instructions for getting set up with [containers](/installation/containers)
quickly.
## GitPod _- beginner friendly!_
If you are having trouble with local installation and Docker confuses you, then
you can launch a **development environment** with the press of a button with
GitPod.
GitPod is an IDE in the cloud which is also attached to a web server. It will
install everything you need. If you are comfortable doing everything from a web
browser, then we have instructions for installing with
[GitPod](/installation/gitpod) as well.

View file

@ -1,135 +0,0 @@
---
title: Vault
---
## Installing
To install Vault follow the
[Vault installation](https://www.vaultproject.io/docs/install) instructions. If
you have a Mac, you can install
[Vault using Homebrew](https://formulae.brew.sh/formula/vault). You can validate
that you have it installed by running the following command.
```bash
vault -h
```
After Vault has been installed, the next step is to start up a Vault server.
```
vault server -dev
```
This will give you an output that looks like this:
```
==> Vault server configuration:
Api Address: http://127.0.0.1:8200
Cgo: disabled
Cluster Address: https://127.0.0.1:8201
Listener 1: tcp (addr: "127.0.0.1:8200", cluster address: "127.0.0.1:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
Log Level: info
Mlock: supported: false, enabled: false
Recovery Mode: false
Storage: inmem
Version: Vault v1.4.2
Version Sha: 18f1c494be8b06788c2fdda1a4296eb3c4b174ce+CHANGES
WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
and starts unsealed with a single unseal key. The root token is already
authenticated to the CLI, so you can immediately begin using Vault.
You may need to set the following environment variable:
$ export VAULT_ADDR='http://127.0.0.1:8200'
The unseal key and root token are displayed below in case you want to
seal/unseal the Vault or re-authenticate.
Unseal Key: <a long key>
Root Token: <a token>
Development mode should NOT be used in production installations!
```
## Configuring
Once Vault is installed and running, the next step is to configure it to work
with your application. Since we want multiple applications (in production) to be
able to use the same Vault infrastructure, we control access with
[Vault policies](https://www.vaultproject.io/docs/concepts/policies) and
different
[key/value secret paths](https://www.vaultproject.io/docs/secrets/kv/kv-v2#setup).
In the following examples, `VAULT_SECRET_NAMESPACE` will be the secret path
where your secrets are stored. This can be any string (except "secrets" since
that exists by default) of your choosing, for example `local-secrets`.
`VAULT_POLICY_NAME` will be the name of the policy that we use to control access
to `VAULT_SECRET_NAMESPACE`. Once again, this should be a string. For example,
`local-policy`.
To set up a policy and secret path run the following commands:
```
vault secrets enable -path=<VAULT_SECRET_NAMESPACE>/ kv-v2
vault policy write VAULT_POLICY_NAME -<<EOF
# grant permission to new VAULT_SECRET_NAMESPACE path
path "VAULT_SECRET_NAMESPACE/data/*" {
capabilities = ["create", "update", "read"]
}
EOF
vault token create -policy=VAULT_POLICY_NAME
```
The final command will give you the following output:
```
# Key Value
# --- -----
# token important-policy-token
# token_accessor another-less-important-token
# token_duration 768h
# token_renewable true
# token_policies ["default" VAULT_POLICY_NAME]
# identity_policies []
# policies ["default" VAULT_POLICY_NAME]
```
The `token` output from above is what you will use in your application to access
Vault. All that is left to do is set the appropriate ENV variables in your
`.env` file.
```shell
export VAULT_TOKEN=important-policy-token export VAULT_SECRET_NAMESPACE=<your
namespace from above>
```
Restart your application to start using Vault. One easy way to see it in action
is via the Rails console.
```ruby
# Enabled Example
[3] pry(main)> AppSecrets["TEST_SET"]="success"
=> "success"
[4] pry(main)> AppSecrets["TEST_SET"]
=> "success"
# Disabled Example
[2] pry(main)> AppSecrets["TEST_SET"]="success"
Vault::MissingTokenError: Missing Vault token! I cannot make requests to Vault without a token. Please
set a Vault token in the client:
Vault.token = "1234"
or authenticate with Vault using the Vault CLI:
$ vault auth ...
or set the environment variable $VAULT_TOKEN to the token value:
$ export VAULT_TOKEN="..."
Please refer to the documentation for more examples.
```

View file

@ -1,297 +0,0 @@
---
title: Windows
---
# Installing Forem on Windows 10
## Installing prerequisites
These prerequisites assume you're working on a `64-bit Windows 10` operating
system machine
- updated to _version 1903, Build 18362_ or _higher_ for **x64 system**
- updated to _version 2004, Build 19041_ or _higher_ for **ARM64 system**
For further information regarding system requirements, head over to the
[Microsoft Docs](https://docs.microsoft.com/en-us/windows/wsl/install-win10#requirements)
or
[Microsoft's blog on extending WSL 2 Support](https://devblogs.microsoft.com/commandline/wsl-2-support-is-coming-to-windows-10-versions-1903-and-1909/).
To check your Windows version, press `Win Logo key` + `R`, type `winver`, then
click OK.
There are other ways to get Forem running on lower versions, but we recommend a
complete WSL 2 installation.
### Installing WSL
Since Forem's codebase is using the Ruby on Rails framework, we will need to
install the Windows Subsystem for Linux (WSL). Some dependencies used by the
source code triggered errors when installing on Windows, so using WSL allows you
to work on the software and not having to fix gem incompatibilities.
Follow the instructions for
[Installing the Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
Once you've installed WSL, complete all the instructions under the following
sections in the link above:
1. [Update to WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install-win10#update-to-wsl-2).
2. [Enable the "Virtual Machine Platform" optional component](https://docs.microsoft.com/en-us/windows/wsl/install-win10#enable-the-virtual-machine-platform-optional-component).
Be sure to reboot your machine after this step.
3. [Set WSL 2 as your default version](https://docs.microsoft.com/en-us/windows/wsl/install-win10#set-wsl-2-as-your-default-version).
Once all the steps mentioned above are completed,
[install Ubuntu 18.04 on Windows](https://www.microsoft.com/store/productId/9N9TNGVNDL3Q).
On your first run, the system will ask for username and password. Take note of
both since it will be used for `sudo` commands. More information about the
process can be found at
[create a user account and password for your new Linux distribution](https://docs.microsoft.com/en-us/windows/wsl/user-support)
### Installing rbenv
`rbenv` is a version manager for Ruby applications which allows one to guarantee
that the Ruby version in development environment matches production. First,
install Ruby language dependencies before installing `rbenv`:
```shell
sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev
```
Now, we install [rbenv](https://github.com/rbenv/rbenv) using the following
commands:
```shell
cd
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL
```
One can verify `rbenv` installation using the `rbenv-doctor` script with the
following commands:
```shell
curl -fsSL https://raw.githubusercontent.com/rbenv/rbenv-installer/main/bin/rbenv-doctor | bash
```
### Installing nvm
As a pre-requisite to install Rails, Node.js needs to be installed.
[nvm](https://github.com/nvm-sh/nvm) is a Node.js version manager that helps a
developer select a specific Node.js version for development.
To install `nvm`, follow the instructions outlined in the
[official nvm documentation](https://github.com/nvm-sh/nvm#install--update-script).
Be sure to reload the shell with the command `exec $SHELL` after the
installation is complete.
Run the following command to verify that `nvm` is installed:
```shell
command -v nvm
```
If the shell outputs `nvm`, the installation is successful. Installation of the
correct Node.js version will be done in a later part of the installation
process.
### Yarn
The fastest way to install Yarn for WSL would be from Debian package repository.
Configure the repository with the following commands:
```shell
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
```
Since we do not have Node.js installed yet, we will be installing Yarn without
the default Node.js with the following command:
```shell
sudo apt update && sudo apt install --no-install-recommends yarn
```
To verify Yarn's installation, run the command `yarn -v`. It should print
`Yarn requires Node.js 4.0 or higher to be installed.`. This indicates that the
Yarn installation succeeded but Node.js still needs to be installed for it to
work fully. We install Node.js later on in the installation process.
### PostgreSQL
Forem requires PostgreSQL version 11 or higher. To Install PostgreSQL on WSL,
follow steps under the
[PostgreSQL APT Repository](https://www.postgresql.org/download/linux/ubuntu/)
section.
Once Installed, perform the following steps in order to set up a username and
password for PostgreSQL:
1. Use `sudo -i service postgresql start` to start the server.
2. Next, replace `$YOUR_USERNAME` in the following commands with your Linux
Username and execute them:
```shell
sudo -u postgres createuser -s $YOUR_USERNAME
createdb
sudo -u $YOUR_USERNAME psql
```
3. You should now be in PostgreSQL's shell interface. Execute `\password` to set
a password for your PostgreSQL user.
4. Be sure to make a note of your username and password for future use. Exit
PostgreSQL by executing the command `\quit`.
For additional configuration options, check our
[PostgreSQL setup guide](/installation/postgresql).
### ImageMagick
Forem uses [ImageMagick](https://imagemagick.org/) to manipulate images on
upload.
ImageMagick can be installed to WSL via installing its
[imagemagick](https://packages.ubuntu.com/bionic/imagemagick) package with the
following command:
```shell
sudo apt update && sudo apt install imagemagick
```
To verify its installation, run `identify -version` command.
### Redis
Forem requires Redis version 6.0 or higher.
We recommend to follow
[this guide](https://redislabs.com/blog/redis-on-windows-10/) to run Redis under
WSL.
## Installing Forem
1. Fork Forem's repository, eg. <https://github.com/forem/forem/fork>
1. Clone your forked repository, eg.
`git clone https://github.com/<your-username>/forem.git`
1. Open the cloned forem folder in terminal with `cd forem`. Next, install Ruby
with the following commands:
```shell
rbenv install $(cat .ruby-version)
rbenv global $(cat .ruby-version)
ruby -v
```
1. Install Node.js with the following set of commands:
```shell
nvm install $(cat .nvmrc)
nvm use $(cat .nvmrc)
node -v
yarn -v
```
1. Install bundler with `gem install bundler`
1. Set up your environment variables/secrets
- Take a look at `.env_sample`. This file lists all the `ENV` variables we
use and provides a fake default for any missing keys.
- If you use a remote computer as dev env, you need to set `APP_DOMAIN`
variable to the remote computer's domain name.
- The [backend guide](/backend) will show you how to get free API keys for
additional services that may be required to run certain parts of the app.
- For any key that you wish to enter/replace:
1. Create `.env` by copying from the provided template (ie. with bash:
`cp .env_sample .env`). This is a personal file that is ignored in git.
1. Obtain the development variable and apply the key you wish to
enter/replace. ie:
```shell
export GITHUB_KEY="SOME_REAL_SECURE_KEY_HERE"
export GITHUB_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
```
- You do not need "real" keys for basic development. Some features require
certain keys, so you may be able to add them as you go.
1. After ensuring that the PostgreSQL server and the Redis server are running,
run `bin/setup`.
> The `bin/setup` script is responsible for installing a varienty of
> dependencies. One can find it inside the `bin` folder by the name of
> `setup`.
>
> - Its first task is to install the `bundler` gem. Next, it will make
> `bundler` install all the gems, including `Rails`, located in `Gemfile`
> in the root of the repository. It also installs `foreman`.
> - It then installs JavaScript dependencies using the script in `bin/yarn`
> file. These dependencies are located in `package.json` in the root of the
> repository.
> - Next, it uses various Rake files located inside the `lib` folder for the
> PostgreSQL database creation and updation.
> - Finally it cleans up all the log files and restarts the Puma server.
### Possible error messages
1. There is a possibility that you might encounter a _statement timeout_ when
seeding the database for the first time. Please increase the value of
`statement_timeout` to `9999999` in `config/database.yml`.
2. If the installation process failed with the following error
`ERROR: Error installing pg`. Please consider installing the following
package `libpq-dev` :
```bash
sudo apt-get install libpq-dev
```
3. If the command `bin/setup` fails at installing `cld-0.8.0` with the warnings
`'aclocal-1.10' is missing on your system` and
`'automake-1.10' is missing on your system`. Please install `automake-1.10`
using the commands below.
```shell
cd
sudo apt-get update
sudo apt-get install autoconf
wget https://ftp.gnu.org/gnu/automake/automake-1.10.tar.gz
tar xf automake-1.10.tar.gz
cd automake-1.10/
./configure --prefix=/usr/local
make
```
### WSL2 and System test
In WSL2, hostname/IP address are no longer shared between Windows and Linux.
There are currently two work-arounds.
1. Use dockerized selenium, ie docker-selenium. You will need docker for the
following steps
1. `docker run -d --name selenium-hub -p 4444:4444 selenium/hub:3.141.59-20200409`
2. `CH=$(docker run --rm --name=ch --link selenium-hub:hub -v /dev/shm:/dev/shm selenium/node-chrome:3.141.59-20200409)`
3. Add `export SELENIUM_URL="http://localhost:4444/wd/hub"` to your `.env`
4. Run your System test!
2. Port forward with `socats` (more info needed).
> If you encountered any errors that you subsequently resolved, **please
> consider updating this section** with your errors and their solutions.
### WSL2 and End to End Tests
In order to run Cypress Test Runner, a few extra setup steps are required to run
GUIs natively inside of WSL2.
[This guide](https://nickymeuleman.netlify.app/blog/gui-on-wsl2-cypress)
explains how to use graphical user interfaces like Cypress inside WSL2.

View file

@ -1,47 +0,0 @@
---
title: Licensing
---
# Licensing
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version. Please see the
[LICENSE](https://github.com/forem/forem/blob/main/LICENSE.md) file in our
repository for the full text.
Like many open source projects, we require that contributors provide us with a
Contributor License Agreement (CLA). By submitting code to the Forem project,
you are granting us a right to use that code under the terms of the CLA.
Our version of the CLA was adapted from the Microsoft Contributor License
Agreement, which they generously made available to the public domain under
Creative Commons CC0 1.0 Universal.
## Where can I learn more about the AGPL-3 license?
Please refer to the official
“[Why the Affero GPL](https://www.gnu.org/licenses/why-affero-gpl.html)” and the
[FAQ page](https://www.gnu.org/licenses/gpl-faq.html) on
[GNU.org](https://www.gnu.org) for full details regarding this license.
## Why does Forem have a CLA?
The Contributor License Agreement (CLA) is a legal document in which you state
that you are entitled to contribute your code to the Forem codebase, and are
willing to have it used in distributions and derivative work.
The CLA helps provide confidence to future users that they will be allowed to
use the Forem codebase without fear that a contributor will try to withdraw
permissions at a later date.
## Am I giving away the copyright of my contributions?
No. You are granting Forem/DEV a license to use and distribute your contribution
without further restriction, not assigning the copyright.
## What if I have other questions?
Please check out our main repository or send an email to
[yo@dev.to](mailto:yo@dev.to).

View file

@ -1,94 +0,0 @@
---
title: Deployment Guide
---
# Deploying Forem
Anyone with the ability to merge PRs on GitHub can deploy the application.
**Whenever a PR is merged the code is deployed. When deploying complex code, be
sure that other team members are around to help if something goes wrong.**
Generally, it's a good idea to keep the SRE team in the loop on high risk
deploys. However, deployments are our collective responsibility, so it's
important to monitor your deploys. You can see deployment status on
Travis-ci.com and in the #deployment-pipeline channel on Slack. Be prepared to
rollback or push a fix for any deployment!
# Deployment and CI/CD Process
## Overview
Forem relies on GitHub and Travis to deploy continuously to Heroku. If a Pull
Request is merged it will automatically be deployed to production once the build
steps complete successfully. The process currently takes about 20 minutes to
complete and will need a few additional minutes before the change goes live.
## Travis Stages
The following stages can be explored in our
[.travis.yml](https://github.com/forem/forem/blob/main/.travis.yml) and
[Procfile](https://github.com/forem/forem/blob/main/Procfile). Our Travis CI
process consists of 2 stages.
1. Running our test suite in 3 parallel jobs.
2. Deploying the application.
### Stage 1: Running Tests
In stage 1, we use [KnapsackPro](https://knapsackpro.com/) to divide our Rspec
tests evenly between 3 different jobs (virtual machines). This ensures that each
job takes relatively the same amount of time to run. After running our Rspec
tests, we then run a series of other checks. These additional checks are split
up between the different jobs. Here is a list of those additional checks that
are run.
- Job 0 is where we run JavaScript tests, Preact tests, and coverage checks.
- Job 1 is where Travis builds Storybook to ensure its integrity, and where we
check for any known vulnerabilities using `bundle-audit`.
- Job 2 is where Travis fires up a Rails console to ensure the application loads
properly.
If all of the jobs pass then we move on to Stage 2 of the Travis CI process.
### Stage 2: Deploying
If the build was kicked off from a pull request being created or updated this
stage will do nothing. If the branch has been merged into main, then this stage
will kick off a deploy. The deploy will run in its own job deploying our
application to Heroku.
Prior to deploying the code, Heroku will run database migrations and do some
final checks (more information on that below) to make sure everything is working
as expected. If these all succeed, then the deploy completes and our team is
notified.
## Deploying to Heroku
We use Heroku's
[Release Phase](https://devcenter.heroku.com/articles/release-phase) feature.
Upon deploy, the app installs dependencies, bundles assets, and gets the app
ready for launch. However, before it launches and releases the app Heroku runs a
release script on a one-off dyno. If that release script/step succeeds the new
app is released on all of the dynos. If that release script/step fails then the
deploy is halted and we are notified.
The name of the script we use is `release-tasks.sh` and its in our root
directory. During this release step we do a few checks.
1. We first check the DEPLOY_STATUS environment variable. In the event that we
want to prevent deploys, for example after a rollback, we will set
DEPLOY_STATUS to "blocked". This will cause the release script to exit with a
code of 1 which will halt the deploy. This ensures that we don't accidentally
push out code while we are waiting for a fix or running other tasks.
2. We run any outstanding migrations. This ensures that a migration finishes
successfully before the code that uses it goes live.
3. We run any data update scripts that need to be run. A data update script is
one that allows us to update data in the background separate from a
migration.
4. Following updating all of our datastores we use the Rails runner to output a
simple string. Executing a Rails runner command ensures that we can boot up
the entire app successfully before it is deployed. We deploy asynchronously,
so the website is running the new code a few minutes after deploy. A new
instance of Heroku Rails console will immediately run a new code.
![](https://devcenter0.assets.heroku.com/article-images/1494371187-release-phase-diagram-3.png)

View file

@ -1,68 +0,0 @@
---
title: Pull Requests
---
# Pull Requests
## General
- We generally follow a branch naming convention of
`<user>/<feature>-<issue number>`, e.g. `sloan/make-things-awesome-5555`.
Issue numbers can be omitted when unavailable, e.g. for things like quick
fixes, spikes, experiments, etc.
- Push early and often, you dont need to wait until youre finished to get
feedback. Please use GitHub draft PRs when you have a work-in-progress (WIP)
PR, or when you have a PR that is contingent on something else to be merged.
We prefer draft PRs rather than adding "DO NOT MERGE" to a PR's title. Any PR
that is not a draft should be ready to be merged by anyone once it has been
reviewed.
- Please follow our pull request template. Provide context for reviewers, when
in doubt err on the side of too much information.
- Core team members are not required to review draft PRs. To explicitly request
feedback, please assign or mention people.
## PR Reviews
PR review is the Core Team's opportunity to weigh-in on changes before they go
into production.
Keep in mind that our team is distributed across the world. It's a good idea to
leave far-reaching PRs open for a day, to give everyone a change to share their
thoughts.
- We require 2 approvals from core team members for each PR:
- One from the same team because they have context and are working towards the
same goal as you.
- One from outside your team for the following reasons:
- A different perspective from someone who has less context
- To leverage our different strengths and garner additional insights
spreading knowledge and code exposure throughout the team
- To avoid overloading individuals
- Tag people that youd like to review your PR using GitHubs Reviewers
function.
- Be kind! The goal here is to work together on shipping good code, not to judge
people.
- For serious issues, use Githubs “Request changes” feature. This should be
reserved for serious problems, e.g.
- Doesnt do what the issue said
- Foreseeable performance problems
- Provable security issues
- Breaks existing functionality
- Everything else can be posted as a comment, but its up to the original PR
author whether or not they want to incorporate the changes.
- Please keep style discussions to an absolute minimum, that time is better
spent making a PR for the configuration of our various lint tools.
- If you make changes to your PR, please re-request feedback from previous
reviewers.
# Merging Pull Requests
At the time of writing, we tend to prefer squashing PRs into a single commit and
merging them. This is easily (and safely) achieved using the GitHub UI.
All required checks such as CI and Code Climate should be green.
Once a PR is merged, it might need to be deployed. Deployment is a team
responsibility, and everyone on the core team should be comfortable deploying
code. For more information, read the
[deployment guide](https://docs.forem.com/maintainers/deployment).

View file

@ -1,12 +0,0 @@
---
title: Maintainers Guide
items:
- pull_requests.md
- deploying.md
---
# Maintainer Processes
This document contains basic instructions for the maintainers of the Forem
application. It's a work in-progress, but should allow the Forem Core Team to
document processes and strategies in a transparent way!

View file

@ -1,17 +0,0 @@
[build]
command = "make"
[environment]
NETLIFY_USE_YARN = 1
# See <https://docs.netlify.com/routing/redirects/redirect-options/> for more options
[[redirects]]
from = "https://docs.dev.to"
to = "https://docs.forem.com"
status = 301
force = true
[[redirects]]
from = "https://docs.dev.to/*"
to = "https://docs.forem.com/:splat"
status = 301
force = true

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