├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── changes_requested.md │ └── config.yml └── workflows │ ├── api-docs-lint.yml │ └── cla.yml ├── .gitignore ├── .node-version ├── .spectral.yml ├── README.md ├── api_v0.yml ├── api_v1.json ├── babel.config.js ├── docs ├── FAQ │ ├── faqs.md │ └── git.md ├── admin │ ├── _category_.json │ ├── admin-guide.md │ └── admin-search.md ├── api.md ├── backend │ ├── _category_.json │ ├── audit-log.md │ ├── auth-apple.md │ ├── auth-facebook.md │ ├── auth-forem.md │ ├── auth-github.md │ ├── auth-google.md │ ├── auth-twitter.md │ ├── authentication.md │ ├── authorization.md │ ├── configuration.md │ ├── data-update-scripts.md │ ├── database.md │ ├── emails.md │ ├── fastly.md │ ├── internationalization.md │ ├── metrics.md │ ├── notifications.md │ ├── push-notifications.md │ ├── roles.md │ ├── scheduled-jobs.md │ ├── service-objects.md │ └── tracking.md ├── contributing-guide │ ├── _category_.json │ ├── api.md │ ├── docs.md │ └── forem.md ├── creators │ ├── _category_.json │ └── configure-forem.md ├── design-guide │ ├── _category_.json │ ├── branding.md │ ├── language.md │ └── theming.md ├── frontend │ ├── _category_.json │ ├── accessibility.md │ ├── debugging.md │ ├── dynamic-imports.md │ ├── instant-click.md │ ├── javascript-approaches │ │ ├── _category_.json │ │ ├── initializers.md │ │ ├── inline-scripts.md │ │ ├── overlaps-and-challenges.md │ │ ├── packs.md │ │ ├── preact.md │ │ └── stimulus-js.md │ ├── linting-formatting.md │ ├── liquid-tags.md │ ├── philosophy.md │ ├── styles.md │ ├── tips.md │ └── tracking.md ├── getting-started │ ├── _category_.json │ ├── branching.md │ ├── commiting.md │ ├── installation │ │ ├── _category_.json │ │ ├── containers.md │ │ ├── gitpod.md │ │ ├── imgproxy.md │ │ ├── linux.md │ │ ├── mac.md │ │ ├── others.md │ │ ├── postgresql.md │ │ ├── vault.md │ │ └── windows.md │ ├── pull-request.md │ ├── start-app.md │ ├── syncing.md │ └── workflow.md ├── intro.md ├── licensing.md ├── maintainers │ ├── _category_.json │ ├── deploying.md │ └── pull_requests.md ├── self-hosting.md ├── technical-overview │ ├── _category_.json │ ├── ab-testing.md │ ├── architecture.md │ ├── caching.md │ ├── compatibility.md │ ├── feature-flags.md │ ├── feed.md │ ├── i18n.md │ └── stack.md ├── tests │ ├── _category_.json │ ├── code-coverage.md │ ├── other-focus-areas │ │ ├── _category_.json │ │ ├── accessibility-tests.md │ │ ├── manual-tests.md │ │ └── regression-tests.md │ ├── philosophy.md │ └── types │ │ ├── _category_.json │ │ ├── e2e-tests.md │ │ ├── feature-tests.md │ │ ├── frontend-tests.md │ │ ├── integration-tests.md │ │ └── unit-functional-tests.md └── troubleshooting.md ├── docusaurus.config.js ├── netlify.toml ├── package.json ├── sidebars.js ├── src └── css │ └── custom.css ├── static ├── .nojekyll └── img │ ├── docs │ ├── admin_feature_flags.png │ ├── backend │ │ ├── add-newapp.png │ │ ├── admin-auth.png │ │ ├── app-secret.png │ │ ├── appleadmin-config.png │ │ ├── callback-url.png │ │ ├── config-facebook.png │ │ ├── create-appid.png │ │ ├── email-source.png │ │ ├── everything-else.png │ │ ├── facebook-login.png │ │ ├── github-keys.png │ │ ├── github-oauth.png │ │ ├── google-1.png │ │ ├── google-10.png │ │ ├── google-11.png │ │ ├── google-12.png │ │ ├── google-2.png │ │ ├── google-3.png │ │ ├── google-4.png │ │ ├── google-5.png │ │ ├── google-6.png │ │ ├── google-7.png │ │ ├── google-8.png │ │ ├── google-9.png │ │ ├── key-download.png │ │ ├── key-registration.png │ │ ├── metrics-datadog.png │ │ ├── pusher-apps-key.png │ │ ├── pusher-create.png │ │ ├── pusher-keys.png │ │ ├── service-id.png │ │ ├── service-registration.png │ │ ├── settings-basic.png │ │ ├── setup-login.png │ │ ├── siteconfig.png │ │ ├── twitter-account-info.png │ │ ├── twitter-account-setup.png │ │ ├── twitter-api-reasons.png │ │ ├── twitter-apply-account.png │ │ ├── twitter-createapp.png │ │ ├── twitter-descript.png │ │ ├── twitter-developer-terms.png │ │ ├── twitter-email-request.png │ │ ├── twitter-enable.png │ │ ├── twitter-env.png │ │ ├── twitter-privacy.png │ │ ├── twitter-terms.png │ │ └── twitter-work.png │ ├── creators │ │ ├── creators-banner.png │ │ ├── creators-data.png │ │ ├── creators-permissions.png │ │ ├── creators-role-single.png │ │ └── creators-settings.png │ ├── frontend │ │ └── debugger.png │ ├── gitpod │ │ ├── fork-button.png │ │ ├── gitpod-forem-login-page.png │ │ ├── gitpod-forem-server-terminal-session.png │ │ ├── gitpod-github-cli-creat-pr.png │ │ ├── gitpod-github-cli-login-ssh.png │ │ ├── gitpod-github-cli-login.png │ │ ├── gitpod-ide.png │ │ ├── gitpod-install-vscode-extensions.png │ │ ├── gitpod-open-site-terminal-session.png │ │ ├── gitpod-prebuild.png │ │ ├── gitpod-preview-browser.png │ │ └── gitpod-pulling-container-image.png │ └── tests │ │ ├── cypress-test-runner.png │ │ └── tests-jest.png │ ├── docusaurus.png │ ├── favicon.ico │ ├── logo.svg │ ├── tutorial │ ├── docsVersionDropdown.png │ └── localeDropdown.png │ ├── undraw_docusaurus_mountain.svg │ ├── undraw_docusaurus_react.svg │ └── undraw_docusaurus_tree.svg └── yarn.lock /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @forem/core-reviewers 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/changes_requested.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Changes Requested 3 | about: Create a report to help us improve a document 4 | --- 5 | 6 | ### Explain the changes 7 | 8 | 9 | 10 | ### Suggested changes 11 | 12 | 13 | 14 | ### Additional context 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Feature Request 4 | url: https://forem.dev/t/features 5 | about: Please use the `Create Post` feature request template to suggest an idea to make Forem better. 6 | - name: Forem Questions and Discussions 7 | url: https://forem.dev/ 8 | about: Please ask and answer questions, discuss features, or reach out for help on forem.dev 9 | - name: Admin Documentation changes 10 | url: https://github.com/forem/admin-docs/issues/new/choose 11 | about: If you would like to create a report to help us improve the Admin docs please visit our admin-docs repository. 12 | -------------------------------------------------------------------------------- /.github/workflows/api-docs-lint.yml: -------------------------------------------------------------------------------- 1 | name: api-docs lint 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Use Node.js 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version-file: '.node-version' 18 | cache: 'yarn' 19 | - run: yarn 20 | - run: yarn api-docs:lint 21 | -------------------------------------------------------------------------------- /.github/workflows/cla.yml: -------------------------------------------------------------------------------- 1 | name: "CLA" 2 | on: 3 | issue_comment: 4 | types: [created] 5 | pull_request_target: 6 | types: [opened,closed,synchronize] 7 | merge_group: 8 | branches: 9 | - main 10 | 11 | permissions: 12 | actions: write 13 | contents: write 14 | pull-requests: write 15 | statuses: write 16 | 17 | jobs: 18 | check_cla: 19 | uses: forem/forem/.github/workflows/cla.yml@main 20 | secrets: inherit 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 18.18.0 2 | -------------------------------------------------------------------------------- /.spectral.yml: -------------------------------------------------------------------------------- 1 | extends: spectral:oas 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Forem Developer Docs 2 | 3 | Welcome to Forem's developer documentation! The docs are built using [Docusaurus 2](https://docusaurus.io/), a React and Markdown based documentation website generator. 4 | 5 | Our developer docs are meant to be used as a way to find instructions to setup a local instance of Forem, documentation on the architecture of Forem, how to contribute, and many other useful documents. 6 | 7 | If you have launched a Forem community of your own and are looking for documentation on how to manage that community, please see our [Admin Docs](https://admin.forem.com/) 8 | 9 | ## Technical Overview 10 | 11 | Most of the configuration here is not too different from the basic `preset-classic` of Docusaurus. The main folders are: 12 | - `docs`: where the documentation lives 13 | - `src`: any custom CSS, components or pages 14 | - `static/img`: any image files 15 | 16 | ## Installation 17 | 18 | ```console 19 | git clone https://github.com/forem/forem-docs.git 20 | yarn install 21 | ``` 22 | 23 | ## Local Development 24 | 25 | Once installed: 26 | 27 | ```console 28 | yarn start 29 | ``` 30 | 31 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 32 | 33 | All relevant Markdown files are in `docs/`. To update any docs, simply update the Markdown file with your favorite Markdown editor. 34 | You can also add new files to the relevant sections. 35 | 36 | For other types of contributions outside of text changes to Markdown files, please see [Docusaurus' docs](https://docusaurus.io/docs) for additional info. 37 | 38 | ## Search 39 | 40 | On production, search is run by [Algolia](https://www.algolia.com), specifically their [DocSearch service](https://docsearch.algolia.com/). 41 | The service scrapes the website every 24 hours or so, so new contributions to the docs may take about that long to update. 42 | 43 | ## Testing Production Builds 44 | 45 | To see if your changes will build (for ex. you made added some screenshots) you can run: 46 | 47 | ```console 48 | yarn build 49 | ``` 50 | 51 | Alternatively, you can make a pull request and use the corresponding deploy preview that builds. 52 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/FAQ/faqs.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 10 3 | --- 4 | 5 | # FAQs 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | **Frequently Asked Questions** 14 | 15 | ## How do I log in after starting up Forem for the first time? 16 | 17 | Seeding the database create an admin user (see 18 | [Database](/backend/database/#default-admin-user)) with the following 19 | credentials: 20 | 21 | ``` 22 | email: admin@forem.local 23 | password: password 24 | ``` 25 | 26 | Once logged in as this admin user, you can turn on any authentication methods 27 | you'd like (see [Authentication](./backend/authentication/)) 28 | 29 | ## How do I build my local copy of the Ruby source code documentation? 30 | 31 | ```shell 32 | cd docs 33 | make ruby-doc 34 | ``` 35 | 36 | Then open `.static/ruby-doc/index.html` in the `docs` directory and browse the 37 | Ruby documentation 38 | 39 | ## How do I enable logging to standard output in development? 40 | 41 | By default Rails logs to `log.development.log`. 42 | 43 | If, instead, you wish to log to `STDOUT` you can add the variable: 44 | 45 | ```shell 46 | export RAILS_LOG_TO_STDOUT=true 47 | ``` 48 | 49 | to your own `.env` file. 50 | 51 | ## How do I see comments in the Feed? 52 | 53 | On the home Feed, we only show comments above certain "score". It's likely the 54 | comments in the local environment will never meet this score. If you want to see 55 | comments locally, you will need to update the score of your local comments 56 | manually. Here's how: 57 | 58 | 1. Open the terminal. 59 | 2. Run `rails dbconsole` to open the PostgreSQL terminal. Alternatively, run 60 | `psql PracticalDeveloper_development` to open `psql`, the PostgreSQL 61 | terminal. 62 | 3. Enter `update comments set score = 30;`. 63 | 4. Type `exit` to leave the PostgreSQL terminal. 64 | 65 | > Note: dbconsole reads database information from config/database.yml which is 66 | > always better since database configs might change in the future. 67 | 68 | Once you refresh the app, you should be able to see some comments in the Feed. 69 | 70 | ## How do I make someone follow me on my local environment? 71 | 72 | In certain cases, for example when testing various functionalities, you may need 73 | to be able to make some user follow you. Here's how: 74 | 75 | 1. Open the rails console by running `rails c` in your terminal. 76 | 2. Get any user you want to follow you, for example `user = User.first`. 77 | 3. Then make this user follow you: `user.follow(your_username)`. 78 | 79 | Boom, you have a new follower! 80 | 81 | ## How do I remove / leave organization I created? 82 | 83 | 1. Open the rails console by running `rails c` in your terminal. 84 | 2. Enter the following commands: 85 | ```ruby 86 | user = User.find_by(username: "your_username") 87 | organization_id = Organization.find_by(slug: "organization_slug").id 88 | user.organization_memberships.where(organization_id: organization_id).destroy_all 89 | ``` 90 | 91 | ## How do I add credits to my account? 92 | 93 | If you ever want to add Listings locally, you must have credits on your account 94 | to "pay" for listing. Here's how: 95 | 96 | 1. Open the rails console `rails console`. 97 | 2. Enter the following commands: 98 | 99 | ```ruby 100 | user = User.find_by(username: "your_username") 101 | Credit.add_to(user, 1000) 102 | ``` 103 | 104 | ^ This will add 1000 credits to your account. But you know, you can't really buy 105 | anything with it :D 106 | 107 | ## How Can I Reduce the Size of My Clones 108 | `forem` clones are currently 1.5G due to old vendor/cache gem's committed to the 109 | git repo. Use the steps below to reduce your clone size by 80% -- meaning 110 | faster _clone_ , deploy , _log_ and less disk usage 111 | 112 | ``` 113 | # do a partial clone 114 | $ git clone --single-branch --branch main --filter=blob:none git@github.com:forem/forem.git 115 | ``` 116 | see [Advanced Git FAQ](./git/) for more details and docs. -------------------------------------------------------------------------------- /docs/FAQ/git.md: -------------------------------------------------------------------------------- 1 | # Reducing Git Repo Size 2 | `forem` clones are currently 1.5G due to old vendor/cache gem's committed to the 3 | git repo. Use the steps below to reduce your clone size by 80% -- meaning 4 | faster _clone_ , deploy , _log_ and less disk usage 5 | 6 | **caveat**: This uses a "partial clone" method which fetches a complete working 7 | tree but avoids cloning older objects from the history . See 8 | [this guide](https://about.gitlab.com/blog/2019/03/13/partial-clone-for-massive-repositories/) 9 | and the [git docs on partial clones](https://git-scm.com/docs/partial-clone) for a 10 | more complete command and config overview. 11 | 12 | ## Preferred Method for New Clones 13 | ``` 14 | # do a partial clone 15 | $ git clone --single-branch --branch main --filter=blob:none git@github.com:forem/forem.git 16 | ``` 17 | ### Test to Confirm the Repo Size 18 | ``` 19 | # your repo will be about 300MB instead of 1500MB 20 | $ du -sh . 21 | 280M . 22 | ``` 23 | 24 | ## Existing Clones (for experts) 25 | You can clean up existing clones, but with this method you will have to rebase 26 | any changes before submitting PRs. Added work will be required. 27 | 28 | #### 1. Update the ref config in `.git/config` 29 | update `fetch` and `partialCloneFilter` 30 | ``` 31 | [remote "upstream"] 32 | url = git@github.com:forem/forem.git 33 | fetch = +refs/heads/main:refs/remotes/upstream/main 34 | promisor = true 35 | partialclonefilter = blob:none 36 | ``` 37 | #### 2. Prune Old Objects Out of your Clone 38 | Prerequisite: [git-filter-repo](https://github.com/newren/git-filter-repo) needs to be installed 39 | ``` 40 | git filter-repo --force --invert-paths --path-glob 'vendor/cache' 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/admin/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Admin Guide", 3 | "position": 8 4 | } -------------------------------------------------------------------------------- /docs/admin/admin-guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Admin Guide 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | 14 | The Forem application contains a rudimentary administration dashboard that lives 15 | behind the admin route. 16 | 17 | The admin dashboard is made up of a series of views that range from 18 | administration tools to simplified reports. These tools are used by users with 19 | the `admin` or `super_admin` roles to administrate the Forem application. 20 | 21 | Authorization for these tools is handled by the [Rolify][rolify] gem. 22 | 23 | Currently, a workaround exists to give access to certain `admin` views via 24 | Rolify by assigning the role `single_resource_admin` to a user. 25 | 26 | `single_resource_admin` users are given access to a Ruby class. In the codebase, 27 | there are admin models, not backed by database tables, that exist for this 28 | purpose. For example, if you needed to give a user access to only 29 | `/admin/apps/welcome`, you'd run the following command in the Rails console: 30 | 31 | ```ruby 32 | user = User.find(some_user_id) 33 | user.add_role(:single_resource_admin, Welcome) 34 | ``` 35 | 36 | This gives the user administration privileges on the controller associated with 37 | an almost empty Rails model that lives in `app/models/admin/welcome.rb`: 38 | 39 | ```ruby 40 | class Welcome < ApplicationRecord 41 | resourcify 42 | # This class exists to take advantage of Rolify for limiting authorization 43 | # on admin reports. 44 | # NOTE: It is not backed by a database table and should not be expected to 45 | # function like a traditional Rails model 46 | end 47 | ``` 48 | 49 | Now that user will be able to access the view at `admin/welcome`. The same 50 | workaround has been implemented for most of the admin views. 51 | 52 | [rolify]: https://github.com/RolifyCommunity/rolify 53 | -------------------------------------------------------------------------------- /docs/admin/admin-search.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Admin Search 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | For admin views that need to take advantage of searching and filtering, we've 14 | chosen to use [Ransack][ransack]. 15 | 16 | Ransack is a Ruby gem that makes searching relatively painless. It has excellent 17 | documentation, but if you're looking for an example of how it's being used on 18 | Forem, we've implemented it to help searching and sorting user reports. 19 | 20 | The view responsible for managing user reports can be found at 21 | `/admin/moderation/reports` and Ransack can be seen in use on the 22 | index action of the [`admin/feedback_messages_controller`][feedback_messages]. 23 | 24 | For Forem, Ransack is being used exclusively in admin, for search problems in 25 | other parts of the app we use [PostgreSQL Full Text Search][postgres_fts]. 26 | 27 | [feedback_messages]: https://github.com/forem/forem/blob/4e41e4a2ac893fa2a6c36990cfe475858ffb086a/app/controllers/admin/feedback_messages_controller.rb#L4 28 | [ransack]: https://github.com/activerecord-hackery/ransack 29 | [postgres_fts]: https://www.postgresql.org/docs/11/textsearch.html 30 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: /api 3 | title: API 4 | --- 5 | 6 | # API 7 | 8 | :::important 9 | 10 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 11 | 12 | ::: 13 | 14 | There are two versions of the API: [version 0](/api/v0) and [version 1](/api/v1). All V0 endpoints are available in V1: we will 15 | be adding V1 documentation for these endpoints in the future. Right now we recommend calling V0 endpoints with `accept` and 16 | `api-key` headers. 17 | 18 | ## [Version 0](/api/v0) 19 | 20 | This is the legacy API. It is deprecated and will be removed in a future release. Clients can continue to use 21 | it as is, but we recommend using the version 1 API. 22 | 23 | ## [Version 1](/api/v1) 24 | 25 | This is the current version of the API. It is the recommended way to interact with Forem. Version 1 requires clients 26 | to use an `accept` header, and many endpoints (but not all) require `api-key` header. 27 | 28 | The `accept` header must be set to `application/vnd.forem.api-v1+json`. 29 | The `api-key` header must be set to the API key for the user. This API key can be generated by the user via their 30 | settings page in Forem. 31 | -------------------------------------------------------------------------------- /docs/backend/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Backend Guide", 3 | "position": 6 4 | } -------------------------------------------------------------------------------- /docs/backend/audit-log.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Audit Log 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | To help maintain accountability for users with elevated permissions the Forem 14 | application has a special model that records certain actions. 15 | 16 | For example, when a user with the `trusted` role creates a negative reaction on 17 | an article, a record is created with certain information about that action. 18 | 19 | That record (which we call an `AuditLog`) looks something like this: 20 | 21 | ```ruby 22 | #"create", "category"=>"vomit", "controller"=>"reactions", "reactable_id"=>"16", "reactable_type"=>"Article"}, 28 | roles: ["trusted"], 29 | slug: "create", 30 | updated_at: Thu, 07 May 2020 20:25:31 UTC +00:00, 31 | user_id: 21> 32 | ``` 33 | 34 | You can see from this record that the user with an id of `21` created a vomit 35 | reaction on the article with an id of `16`. If that's not obvious to you from 36 | this object, don't worry, just take our word on it. 37 | 38 | You can find an example of `Audit::Logger` in action in 39 | `app/controllers/admin/reactions_controller.rb`: 40 | 41 | ```ruby 42 | after_action only: [:update] do 43 | Audit::Logger.log(:moderator, current_user, params.dup) 44 | end 45 | ``` 46 | 47 | This code creates a record to indicate that a someone modified a reaction from 48 | the admin controller. 49 | 50 | It's a good idea to add a similar `after_action` to any controller action that 51 | might benefit from increased transparency. 52 | 53 | Additionally, the `AuditLog` is used to track important actions performed on a 54 | user's account, e.g. adding or removing a credit card: 55 | 56 | ```ruby 57 | # { 58 | category: "user.credit_card.edit", 59 | created_at: Tue, 21 Jul 2020 06:35:13 +03 +03:00, 60 | data: { 61 | "action" => "create", 62 | "controller" => "stripe_active_cards", 63 | "user_action" => "add" 64 | }, 65 | id: 4, 66 | roles: [], 67 | slug: "credit_card_add", 68 | updated_at: Tue, 21 Jul 2020 06:35:13 +03 +03:00, 69 | user_id: 53 70 | } 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/backend/auth-apple.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Apple Authentication 3 | sidebar_position: 7 4 | --- 5 | 6 | # Sign in with Apple Authentication 7 | 8 | :::important 9 | 10 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 11 | 12 | ::: 13 | 14 | Forem allows you to authenticate using 15 | [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/). In order 16 | to use this authentication method you'll need to be enrolled to the 17 | [Apple Developer Program](https://developer.apple.com/programs/) in order to 18 | retrieve the necessary credentials and an HTTPS supported URL for the callback 19 | configuration (HTTP won't work). Then you'll need to provide the keys to the 20 | Rails application. 21 | 22 | # Apple Developer Portal Configuration 23 | 24 | [Register/Sign in](https://developer.apple.com/account) to your Apple Developer 25 | Account. 26 | 27 | ## Service ID Configuration 28 | 29 | 1. [Create a Service ID](https://developer.apple.com/account/resources/identifiers/list/serviceId). 30 | 31 | ![Create Service ID](https://user-images.githubusercontent.com/6045239/92610177-a5cc9e00-f274-11ea-9f63-20d8356d0bee.png) 32 | 33 | 2. Name the Service and finalize the registration. 34 | 35 | ![Naming Service ID](https://user-images.githubusercontent.com/6045239/92610168-a36a4400-f274-11ea-8f79-7516c0c6c9c3.png) 36 | 37 | 3. Configure Domains and Subdomains & the callback URL. This example uses 38 | [ngrok](https://ngrok.io) for HTTPS support. 39 | 40 | ![Callback URLS](https://user-images.githubusercontent.com/6045239/92610184-a8c78e80-f274-11ea-9439-a98c6b627567.png) 41 | 42 | ## Key Configuration 43 | 44 | 1. [Register a new Key](https://developer.apple.com/account/resources/authkeys/add). 45 | Enable the "Sign in with Apple" option and configure it so it's associated 46 | with the corresponding App ID. 47 | 48 | ![Register a new Key](https://user-images.githubusercontent.com/6045239/92611125-b3ceee80-f275-11ea-9c00-e1b5ca2f9af0.png) 49 | 50 | 2. Download the Key. 51 | 52 | ![Download the Key](https://user-images.githubusercontent.com/6045239/92611466-0f00e100-f276-11ea-912d-f8a74b6dfb04.png) 53 | 54 | # Configuring the Rails Application 55 | 56 | Now with both the Service ID and Key you'll need to enable Apple Authentication 57 | and pass in the credentials in the admin dashboard 58 | `/admin/customization/config`. 59 | 60 | ![Admin Authentication Configuration Dashboard](https://user-images.githubusercontent.com/6045239/133795693-56573842-387c-46e6-8326-d64808d571cd.png) 61 | 62 | Fill in each of the configuration fields. 63 | 64 | Make sure the PEM key is copied exactly the way it appears in the file you downloaded from the Apple Developer portal, including newlines. If this isn't done correctly users will encounter an `Invalid curve name` error. **Make sure a newline is added at the very end of the Apple PEM field**. 65 | 66 | ![Apple config](https://user-images.githubusercontent.com/6045239/133795521-246ede88-15f0-45d1-a060-5e7d29e77568.png) 67 | 68 | Save the changes and you're ready. 69 | 70 | ## Email configuration 71 | 72 | Apple uses what they call Private Email Relay Service to hide user's emails. For 73 | this to work first 74 | [create a new email source](https://developer.apple.com/account/resources/services/list). 75 | 76 | ![Email configuration](https://user-images.githubusercontent.com/6045239/92612469-22607c00-f277-11ea-918d-697cf4a18b15.png) 77 | 78 | Emails sent need to be authenticated and the configuration depends on the 79 | different providers available: 80 | 81 | - [Mailchimp](https://mailchimp.com/help/set-up-custom-domain-authentication-dkim-and-spf/) 82 | - [SendGrid](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) 83 | - [SES](https://docs.aws.amazon.com/es_es/ses/latest/DeveloperGuide/send-email-authentication-dkim.html) 84 | -------------------------------------------------------------------------------- /docs/backend/auth-facebook.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Facebook Authentication 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Forem allows you to authenticate using Facebook. In order to use this 14 | authentication method in local development, you will need to setup a Facebook 15 | App and retrieve its keys. Then you'll need to provide these keys to the Rails 16 | application. 17 | 18 | ## Sign up 19 | 20 | 1. [Sign in](https://facebook.com) to your Facebook account. 21 | 22 | 2. In order to get the API keys, you will have to 23 | [convert your account to a developer account](https://developers.facebook.com/). 24 | 25 | ## Get API keys 26 | 27 | 1. [Sign up](#facebook-sign-up) or [sign in](https://developers.facebook.com) to 28 | your Facebook developer account. 29 | 30 | 2. From **My Apps** dashboard, click on **Add a New App**. 31 | 32 | ![Add new app](/img/docs/backend/add-newapp.png) 33 | 34 | 3. Select **For Everything Else**. 35 | 36 | ![Select For Everything Else](/img/docs/backend/everything-else.png) 37 | 38 | 4. Fill in the app display name and contact email, then click on **Create App 39 | ID**. 40 | 41 | ![Create App Id](/img/docs/backend/create-appid.png) 42 | 43 | 5. On the **Add a Product** screen, click **Set Up** under the **Facebook 44 | Login** section. 45 | 46 | ![Click Set Up on Facebook Login](/img/docs/backend/setup-login.png) 47 | 48 | 6. Ignore the quickstart options, and click **Settings -> Basic** in the sidebar. 49 | 50 | ![Click settings/basic](/img/docs/backend/settings-basic.png) 51 | 52 | 7. From the basic settings screen dashboard copy the **App ID** and **App 53 | Secret** values to your `.env` file accordingly (name of Facebook key -> name 54 | of our `Settings::General` variable). 55 | 56 | ```text 57 | APP ID -> FACEBOOK_APP_ID 58 | API secret -> FACEBOOK_APP_SECRET 59 | ``` 60 | 61 | ![Add app id and app secret](/img/docs/backend/app-secret.png) 62 | 63 | ## Configure the Facebook App 64 | 65 | 1. From the basic settings screen dashboard set your application's domain name 66 | in **App Domains** field, and be sure to click **Save Changes**. 67 | 68 | ![Configure the facebook app](/img/docs/backend/config-facebook.png) 69 | 70 | 2. Naviate to **Facebook Login --> Settings**, and enter the following callback 71 | URL in the field **Valid OAuth Redirect URIs**: 72 | 73 | `https://>/users/auth/facebook/callback` 74 | 75 | ![Enter valid OAuth redirect URI](/img/docs/backend/facebook-login.png) 76 | -------------------------------------------------------------------------------- /docs/backend/auth-forem.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Forem Authentication 3 | sidebar_position: 4 4 | --- 5 | 6 | # Forem Authentication 7 | 8 | :::important 9 | 10 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 11 | 12 | ::: 13 | 14 | Forem allows you to authenticate using the [Forem Passport](https://passport.forem.com) service. Enabling this authentication provider is highly encouraged in order to benefit from the many Forem Ecosystem integrations (i.e. mobile apps). 15 | 16 | Better integration with the `/admin` dashboard is currently under development but in the mean time you will be able to enable the Forem Authentication provider with the following steps: 17 | 18 | 1. Create an account in https://passport.forem.com 19 | 2. Use the [Forem Creator Setup](https://passport.forem.com/oauth/applications) to create new credentials for your Forem site. 20 | - You will receive the `Key` and `Secret` credentials for your site. 21 | - The instance logo will take a few moments to sync. 22 | 3. Enable the Forem Authentication provider in your Forem admin dashboard. 23 | 24 | ![Forem Creator Tools](https://user-images.githubusercontent.com/6045239/154768196-b9a2012d-1af0-404b-bb69-61d3c6606991.png) 25 | -------------------------------------------------------------------------------- /docs/backend/auth-github.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # GitHub Authentication 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Forem allows you to authenticate using GitHub. To use this authentication method 14 | in local development, you will need to set up a GitHub App and retrieve its 15 | keys. Then you'll need to provide these keys to the Rails application. 16 | 17 | 1. [Click this link to create a new OAuth application in your Github account](https://github.com/settings/applications/new) - 18 | you will be redirected to sign in to Github account if you have not already. 19 | 20 | 2. Fill in the form with an application name, description, and the URL 21 | `http://localhost:3000/users/auth/github/callback`. Replace the port `3000` if you run Forem on another 22 | port. 23 | 24 | ![Fill in form](/img/docs/backend/github-oauth.png) 25 | 26 | 3. You will be redirected to the app's **Developer settings**. Here you will 27 | find the keys. Add them to your `.env` file accordingly (name of GitHub key 28 | -> name of our `ENV` variable): 29 | 30 | ```text 31 | Client ID -> GITHUB_KEY 32 | Client Secret -> GITHUB_SECRET 33 | ``` 34 | 35 | ![Add api keys to .env file](/img/docs/backend/github-keys.png) 36 | 37 | 4. Done. 38 | -------------------------------------------------------------------------------- /docs/backend/auth-google.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Google OAuth2 Authentication 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Forem allows you to authenticate using Google OAuth2. To use this authentication method 14 | in local development, you will need to set up Google client application and retrieve its 15 | keys. Then you'll need to provide these keys to the Rails application. 16 | 17 | ## Google Admin Console. 18 | 19 | 1. [Click this link to create a new OAuth application in your Google console](https://console.cloud.google.com/projectcreate) - 20 | you will be redirected to sign in to Google account if you have not already. 21 | 22 | 2. Go to **OAuth Consent Screen** section. Set the **Publishing Status** to **Testing**. 23 | 24 | ![OAuth Consent Section](/img/docs/backend/google-1.png) 25 | 26 | ![Publishing Status](/img/docs/backend/google-2.png) 27 | 28 | 3. Add your test user email to the **Test Users** section. 29 | 30 | ![Add Test Users](/img/docs/backend/google-3.png) 31 | 32 | 4. Go to the **Credentials** section and at the top of the screen click **Create Credentials** -> **OAuth Client ID**. 33 | 34 | ![Adding OAuth Credentials](/img/docs/backend/google-4.png) 35 | 36 | 5. Select **Web Application** as the **Application Type**, and name it. 37 | 38 | ![Naming OAuth Application](/img/docs/backend/google-5.png) 39 | 40 | 6. Under **Authorized Redirect URIs** add the URI `http://localhost:3000/users/auth/google_oauth2/callback`. Replace the port `3000` 41 | if you run Forem on another port. 42 | 43 | ![Create URI Redirect](/img/docs/backend/google-6.png) 44 | 45 | 7. On the resulting screen you'll find your app's **Client ID** and **Client Secret**. Make a note of them. 46 | 47 | ![Client ID and Key](/img/docs/backend/google-7.png) 48 | 49 | 8. Click **OAuth Consent Screen** in the sidebar, then click **Edit App** next to your app's name. 50 | 51 | ![Consent Screen](/img/docs/backend/google-9.png) 52 | 53 | 9. Fill out the information on the resulting **Edit App Registration** screen, then click **Next Step**. 54 | 55 | ![Editing App Registration](/img/docs/backend/google-10.png) 56 | 57 | 10. On the next screen, **Scopes**, click **Add or Remove Scopes**. 58 | 59 | ![Add Scopes](/img/docs/backend/google-11.png) 60 | 61 | 11. Click the checkboxes next to the first two unlabeled scopes in the **Updated Selected Scopes** section. 62 | 63 | ![Update Selected Scopes](/img/docs/backend/google-12.png) 64 | 65 | 12. Complete the rest of the app registration screens. 66 | 67 | ## Your Forem 68 | 69 | 13. Enable the Google Authentication provider in the admin dashboard, entering the **Client ID** and **Client Key** from step 7. 70 | 71 | ![Forem Admin Dashboard](/img/docs/backend/google-8.png) 72 | 73 | 14. Done. 74 | -------------------------------------------------------------------------------- /docs/backend/auth-twitter.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Twitter Authentication 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Forem allows you to authenticate using Twitter. In order to use this 14 | authentication method in local development, you will need to setup a Twitter App 15 | and retrieve its keys. Then you'll need to provide these keys to the Rails 16 | application. 17 | 18 | ## Sign up 19 | 20 | 1. [Sign in](https://developer.twitter.com/apps) to your Twitter account. 21 | 22 | 2. In order to get the API keys, you will have to 23 | [apply for a developer account](https://developer.twitter.com/en/apply-for-access). 24 | Click the **Apply** button. 25 | 26 | ![Apply for developer account](/img/docs/backend/twitter-apply-account.png) 27 | 28 | 3. Setup your Twitter account. Be sure you have your phone number and email 29 | address filled in. 30 | 31 | ![Set up Twitter account](/img/docs/backend/twitter-account-setup.png) 32 | 33 | 4. Fill in your account information and give a name to your **developer 34 | account**. 35 | 36 | ![Fill in your account information](/img/docs/backend/twitter-account-info.png) 37 | 38 | 5. Write down the reasons that you want to use Twitter API. Mention Forem's 39 | community and describe the issues and tests and things that you want to work 40 | on. Copy it, you might use it later. ;) 41 | 42 | ![twitter-up-4](/img/docs/backend/twitter-api-reasons.png) 43 | 44 | 6. Read :) and accept the Terms and Conditions. 45 | 46 | ![twitter-up-5](/img/docs/backend/twitter-terms.png) 47 | 48 | 7. Verify your email address once more, and you will be done. 49 | 50 | 8. You are done. 51 | 52 | ## Get API keys 53 | 54 | 1. [Sign up](#twitter-sign-up) or [sign in](https://developer.twitter.com/apps) 55 | to your Twitter developer account. 56 | 57 | 2. From **Apps** dashboard, click on **Create and app**. 58 | 59 | ![Click Create and app](/img/docs/backend/twitter-createapp.png) 60 | 61 | 3. Fill in the app name, description, and URL `https://dev.to`. 62 | 63 | ![Add name, description, and URL](/img/docs/backend/twitter-descript.png) 64 | 65 | 4. Check the **Enable Sign in with Twitter** option and fill in the Callback URL 66 | `http://localhost:3000/users/auth/twitter/callback` (or whatever port you run 67 | Forem on). 68 | 69 | ![Enable sign-in](/img/docs/backend/twitter-enable.png) 70 | 71 | 5. Fill in the information, **Terms of Service** `http://dev.to/terms` and 72 | **Privacy policy** `http://dev.to/privacy`. 73 | 74 | ![Agree to terms and privacy](/img/docs/backend/twitter-privacy.png) 75 | 76 | 6. Write down (or paste) the things that you will work on. Press **Create**. 77 | 78 | ![Describe what you will work on](/img/docs/backend/twitter-work.png) 79 | 80 | 7. Review the 81 | [Twitter Developer Terms](https://developer.twitter.com/en/developer-terms/agreement-and-policy.html) 82 | and agree to do nothing sketchy. 83 | 84 | ![Agree to Developer Terms](/img/docs/backend/twitter-developer-terms.png) 85 | 86 | 8. The app is all set! 87 | 88 | 9. One more change: From the app dashboard, go to **Permissions** and check 89 | **Request email addresses from users** option. 90 | 91 | ![check Request email addresses from users](/img/docs/backend/twitter-email-request.png) 92 | 93 | 10. From the same dashboard access the **Keys and tokens** and add them to your 94 | `.env` file accordingly (name of Twitter key -> name of our `ENV` variable). 95 | Be sure to copy the _access token_ and _access token secret_ right away 96 | because it will be hidden from you in the future. 97 | 98 | ```text 99 | API key -> TWITTER_KEY 100 | API secret key -> TWITTER_SECRET 101 | ``` 102 | 103 | ![Add Twitter Key and Secret to .env file](/img/docs/backend/twitter-env.png) 104 | -------------------------------------------------------------------------------- /docs/backend/authentication.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Authentication 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Authentication is handled by [Devise](https://github.com/plataformatec/devise) 14 | and [OmniAuth](https://github.com/omniauth/omniauth). 15 | 16 | On Forem you can authenticate through Facebook, GitHub, Twitter or Apple. Please check out 17 | the respective guides on how to authenticate: 18 | 19 | - [Forem authentication](auth-forem) 20 | - [Apple authentication](auth-apple) 21 | - [Facebook authentication](auth-facebook) 22 | - [Github authentication](auth-github) 23 | - [Google authentication](auth-google) 24 | - [Twitter authentication](auth-twitter) 25 | 26 | We may add other authentication providers in the future. Please check back 27 | again, or search 28 | [our GitHub repository's issues.](https://github.com/forem/forem/issues) 29 | -------------------------------------------------------------------------------- /docs/backend/authorization.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Authorization 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Authorization is handled by the third-party gem 14 | [Pundit](https://github.com/varvet/pundit) through the `authorize` method which 15 | you can find in various controllers, look for statements like: 16 | 17 | ```ruby 18 | authorize @user 19 | ``` 20 | 21 | All authorization policies can be found in `/app/policies`. 22 | -------------------------------------------------------------------------------- /docs/backend/configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 8 3 | --- 4 | 5 | # Configuration 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | We currently use the following gems for configuring the application: 14 | 15 | - [dotenv](https://github.com/bkeepers/dotenv) 16 | - [rails-settings-cached](https://github.com/huacnlee/rails-settings-cached) 17 | - [vault](https://github.com/hashicorp/vault-ruby) 18 | 19 | ## dotenv 20 | 21 | This gem is used for configuring environment variables for test and development 22 | environments. Examples: 23 | 24 | - `REDIS_URL` 25 | - `FASTLY_API_KEY` 26 | - `STRIPE_SECRET_KEY` 27 | 28 | Settings managed via your ENV can be found in 29 | [installation section of your operating system](/getting-started/installation/mac#installing-forem) and viewed at 30 | `/admin/customization/config` (see [the Admin guide](../admin/admin-guide)): 31 | 32 | ![Screenshot of env variable admin interface](/img/docs/backend/siteconfig.png) 33 | 34 | Tests always load the sample environment (and ignore the .env file). If you need to customize tests, add the environment variables to a file named .env.test.local. This prevents your development secrets from being present during test execution. 35 | 36 | ## rails-settings-cached 37 | 38 | We use this gem for managing settings used within the app's business logic. 39 | Examples: 40 | 41 | - `Settings::General.main_social_image` 42 | - `Settings::RateLimit.follow_count_daily` 43 | - `Settings::Authentication.twitter_secret` 44 | 45 | These settings can be accessed via the 46 | [`Settings::General`](https://github.com/forem/forem/blob/main/app/models/settings/general.rb) 47 | object and various models in the `Settings::` namespace and viewed or modified 48 | via `/admin/customization/config` (see [the Admin guide](../admin/admin-guide)). 49 | 50 | ![Screenshot of site configuration admin interface](https://user-images.githubusercontent.com/47985/73627238-6276d500-467e-11ea-8724-afb703f056bc.png) 51 | 52 | ## Vault 53 | 54 | The vault Ruby gem allows us to interact with 55 | [Vault](https://www.vaultproject.io/docs/what-is-vault). In a nutshell, Vault is 56 | a tool for securely storing and accessing secrets. It is completely optional for 57 | running a Forem. To access it we use the wrapper `AppSecrets`. 58 | 59 | ```ruby 60 | class AppSecrets 61 | def self.[](key) 62 | result = Vault.kv(namespace).read(key)&.data&.fetch(:value) if ENV["VAULT_TOKEN"].present? 63 | result ||= ApplicationConfig[key] 64 | 65 | result 66 | rescue Vault::VaultError 67 | ApplicationConfig[key] 68 | end 69 | 70 | def self.[]=(key, value) 71 | Vault.kv(namespace).write(key, value: value) 72 | end 73 | 74 | def self.namespace 75 | ENV["VAULT_SECRET_NAMESPACE"] 76 | end 77 | private_class_method :namespace 78 | end 79 | ``` 80 | 81 | We attempt to access a secret from Vault if it is enabled, i.e. if the 82 | `VAULT_TOKEN` is present. If Vault is not enabled or if we cannot find the 83 | secret in it, then we fallback to fetching the secret from the 84 | `ApplicationConfig`. 85 | 86 | One advantage of using Vault with Forem is that it allows you to update your 87 | secrets easily through the application rather than having to mess with ENV 88 | files. If you would like to try out Vault, follow our 89 | [installation guide for setting it up locally](/getting-started/installation/vault). 90 | -------------------------------------------------------------------------------- /docs/backend/data-update-scripts.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 9 3 | --- 4 | 5 | # Data Update Scripts 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## What are Data Update Scripts? 14 | 15 | Data Update Scripts were introduced in 16 | [this PR](https://github.com/forem/forem/pull/6025) and allow us to run any data 17 | updates we might need. For example, if we added a column to the database and 18 | then wanted to backfill that column with data, rather than going and manually 19 | doing it in a console, we would use a `DataUpdateScript`. 20 | 21 | ## How it works 22 | 23 | First off, we added a 24 | [DataUpdateScript model](https://github.com/forem/forem/blob/main/app/models/data_update_script.rb) 25 | to Rails and a corresponding database table. This table is what we use to keep 26 | track of what scripts have been run and which ones have not/still need to be. 27 | 28 | To create a script you can use our custom Rails generator: 29 | 30 | ``` 31 | rails generate data_update BackfillColumnForArticles 32 | ``` 33 | 34 | This will create a simple Ruby class like below and all you have to do is fill 35 | in the code it will run. 36 | 37 | ```ruby 38 | module DataUpdateScripts 39 | class BackfillColumnForArticles 40 | def run 41 | # Place your data update logic here 42 | # Make sure your code is idempotent and can be run safely 43 | # multiple times at any time 44 | end 45 | end 46 | end 47 | ``` 48 | 49 | The generator will also automatically create the corresponding spec file. 50 | 51 | ```ruby 52 | require "rails_helper" 53 | require Rails.root.join( 54 | "lib/data_updates/20201103042915_backfill_column_for_articles.rb", 55 | ) 56 | 57 | describe DataUpdateScripts::BackfillColumnForArticles do 58 | pending "add some examples to (or delete) #{__FILE__}" 59 | end 60 | ``` 61 | 62 | While we encourage adding tests for data update scripts, you can skip spec 63 | creation by adding the `--no-spec` option to the `rails generate` command: 64 | 65 | ``` 66 | rails generate data_update BackfillColumnForArticles --no-spec 67 | ``` 68 | 69 | Once your script is in place then you can either run `rails data_updates:run` 70 | manually or you can let our setup script handle it. In our local 71 | [bin/setup](https://github.com/forem/forem/blob/main/bin/setup) script you will 72 | see we have added an additional task to update data. This kicks off the rake 73 | task `data_updates:run` for you. 74 | 75 | The rake task itself will check the `lib/data_update_scripts` folder to see if 76 | there are any new scripts that need to be run. It does this by reading all of 77 | the files and then checking to see if they have a corresponding database entry. 78 | If they do not, then we create a new one and run the script. If a database entry 79 | already exists and it indicates the script has been run, then we skip that 80 | script. 81 | 82 | ## In production 83 | 84 | `DataUpdateScripts` are also run automatically when a production deploy goes out. 85 | However, to ensure the new code they need to use has been deployed we use a 86 | [`DataUpdateWorker`](https://github.com/forem/forem/blob/main/app/workers/data_update_worker.rb) 87 | via Sidekiq and set it to run 10 minutes after the deploy script has completed. 88 | 89 | ## Best practices 90 | 91 | ### Working with large collections of rows 92 | 93 | From time to time, scripts need to operate on a large amount of rows; in those 94 | cases we encourage: 95 | 96 | - adding explicit logging to the script. 97 | - reversing the order, to start processing the most recent records first. 98 | 99 | For example: 100 | 101 | ```ruby 102 | def run 103 | Article.find_each(order: :desc).with_index do |article, index| 104 | Rails.logging.info("...") if index % 1000 == 0 # this will log every 1000 articles 105 | 106 | article.save 107 | end 108 | end 109 | ``` 110 | -------------------------------------------------------------------------------- /docs/backend/database.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 0 3 | --- 4 | 5 | # Additional Database options 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## Seed Data 14 | 15 | By default, the amount of articles and users generated is quite tiny so that 16 | contributors experience a quick installation. If you require more data for your 17 | local installation, you can tune amount of data generated with the environment 18 | variable `SEEDS_MULTIPLIER`. 19 | 20 | This variable, which defaults to `1`, allows the developer to increase the size 21 | of their local DB. For example: 22 | 23 | ```shell 24 | SEEDS_MULTIPLIER=2 rails db:setup 25 | ``` 26 | 27 | Will result in twice the default amount of items being created in the database. 28 | 29 | It's currently used only for `articles` and `users`. 30 | 31 | It can also be used for `rails db:seed` and `rails db:reset`. 32 | 33 | ## Default Admin User 34 | 35 | Seed data creates a handful of regular users, and a single admin user that can 36 | be used to log into the application with the Email login option: 37 | 38 | ``` 39 | email: admin@forem.local 40 | password: password 41 | ``` 42 | 43 | ### Other seed modes 44 | 45 | To put your local forem into "starter mode", as it would be for a new creator, 46 | use `MODE=STARTER` i.e... 47 | 48 | ```shell 49 | MODE=STARTER rails db:setup 50 | ``` 51 | This mode skips creation of all sample data. 52 | -------------------------------------------------------------------------------- /docs/backend/emails.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 10 3 | --- 4 | 5 | # Emails 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | **Setting up email** 14 | 15 | If you would like to enable transactional email using services like SendGrid or 16 | Mailgun, you can configure it by using the following environment variables: 17 | 18 | ```shell 19 | SMTP_ADDRESS= # ie. "smtp.sendgrid.net" 20 | SMTP_PORT= # ie. 587 21 | SMTP_DOMAIN= 22 | SMTP_USER_NAME= 23 | SMTP_PASSWORD= 24 | SMTP_AUTHENTICATION= # defaults to :plain 25 | ``` 26 | 27 | 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). 28 | 29 | # Previewing emails in development 30 | 31 | You can view email previews at . 32 | 33 | Previews are setup in the directory `spec/mailers/previews`. 34 | 35 | # Overriding mailer templates 36 | 37 | To update the contents of emails that the app sends, edit the views under 38 | `app/views/mailers`. Note that this app uses the 39 | [`devise_invitable`](https://github.com/scambra/devise_invitable) gem for 40 | invitations. The views for this gem are stored under `app/views/devise/mailer`. 41 | -------------------------------------------------------------------------------- /docs/backend/fastly.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 11 3 | --- 4 | 5 | # Fastly 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## What is Fastly? 14 | 15 | [Fastly](https://www.fastly.com/) is a third party service we use for caching on 16 | the edge. It allows us to scale up and serve our millions of visitors quickly 17 | and efficiently. 18 | 19 | If you want to learn more about we use Fastly, check out this 20 | [talk](https://www.youtube.com/watch?v=Afy7H04X9Us) that one of our founders, 21 | [@benhalpern](https://dev.to/ben), gave at RailsConf 2018 talking about how we 22 | made our app so fast it went viral. 23 | 24 | Fastly offers many different configurations (conditions, 25 | [snippets](https://docs.fastly.com/vcl/vcl-snippets/about-vcl-snippets/), etc.). 26 | 27 | ## Snippets 28 | 29 | Snippets, in general, are _"short blocks of VCL logic that can be included 30 | directly in your service configurations. They're ideal for adding small sections 31 | of code when you don't need more complex, specialized configurations that 32 | sometimes require custom VCL."_ 33 | 34 | You can learn more about VCL snippets on 35 | [Fastly's website](https://docs.fastly.com/vcl/vcl-snippets/about-vcl-snippets/) 36 | 37 | ### Adding query string parameters to a safe list 38 | 39 | In the context of contributing, here's what you need to know about Fastly. In 40 | order for our servers to receive any sort of query string parameters in a 41 | request, they must first be marked as safe in Fastly. For example, if you're 42 | creating a new API endpoint or updating an existing one to accept new 43 | parameters, you'll need to update Fastly. 44 | 45 | The reason we have a safe list of parameters in Fastly this way is so we don't 46 | have to consider junk parameters when busting the caches. Check out our 47 | [`EdgeCache` services](https://github.com/forem/forem/tree/main/app/services/edge_cache) 48 | to see examples of this. 49 | 50 | Previously this was a manual process done by an internal team member. Now we do 51 | it programmatically using the Fastly 52 | [gem](https://github.com/fastly/fastly-ruby). 53 | 54 | ### How it works 55 | 56 | We created a new file, `config/fastly/safe_params_list.vcl`, to house all of the 57 | safe params in Fastly. 58 | 59 | If you need to update this list, simply update this file. It's as easy as that! 60 | This is a VCL file and you'll see a little Regex checking for a list of safe 61 | params. 62 | 63 | _Fastly is not setup for development._ 64 | 65 | ### In production 66 | 67 | If you have Fastly configured in Production, all Fastly configs are updated 68 | automatically when a production deploy goes out. 69 | 70 | We do this by executing `bin/rails fastly:update_configs` in our 71 | `release-tasks.sh` script. 72 | -------------------------------------------------------------------------------- /docs/backend/metrics.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 17 3 | --- 4 | 5 | # Metrics 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## Time series data 14 | 15 | We track point-in-time data by sending data points to Datadog using 16 | [Statdsd](https://github.com/DataDog/dogstatsd-ruby). 17 | 18 | Currently, we run a daily metric fetching job in `fetch.rake` which is called 19 | via the Heroku scheduler. If we wanted to do other frequencies like hourly, etc. 20 | we could add similar tasks. 21 | 22 | The rake task calls `Metrics::RecordDailyUsageWorker.perform_async`, which 23 | performs algorithms to come up with data points to send, for example: 24 | 25 | ```ruby 26 | DataDogStatsClient.count("users.active_days_past_week", one_day_users, tags: { resource: "users", group: "new_users, day_count: 1 }) 27 | ``` 28 | 29 | If you want to create a new periodic data send, follow this pattern to do so. 30 | 31 | ## Vendor-Agnostic 32 | 33 | While we currently are not vendor-agnostic in how we do this (Heroku/Datadog), 34 | it is set up in a way that could become so in the future. The main pattern is 35 | `Every x minutes/hours/etc. send aggregate data to warehouse where it can be examined on a timeseries basis`. 36 | This could, in the future, be bundled right into the platform using an open 37 | source timeseries database and data visualization. 38 | 39 | Once in Datadog, dashboards can created using 40 | 41 | ![Datadog metrics](/img/docs/backend/metrics-datadog.png) 42 | -------------------------------------------------------------------------------- /docs/backend/notifications.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 15 3 | --- 4 | 5 | # Notifications 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Since notifications are run asynchronously, we'll want to make sure jobs are 14 | running: `bundle exec sidekiq`. If that's not running, you won't receive any 15 | notifications. You might need to create another account to pass notifications 16 | back and forth if you're doing this all through the UI. 17 | 18 | Otherwise, you can generate notifications from the Rails console and run the 19 | class methods from `app/models/notification.rb`. For example: 20 | 21 | ```ruby 22 | # follow notification 23 | me = User.last 24 | follow = User.first.follow(me) 25 | Notification.send_new_follower_notification_without_delay(follow) 26 | # reaction notification 27 | rxn = Reaction.create( 28 | user_id: 1, 29 | category: "like", 30 | reactable: me.articles.last, # this assumes you have an article written 31 | ) 32 | Notification.send_reaction_notification_without_delay(rxn, me) 33 | ``` 34 | 35 | Notice you have to run these methods `without_delay` since this is assuming jobs 36 | are not running. 37 | -------------------------------------------------------------------------------- /docs/backend/push-notifications.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 18 3 | --- 4 | 5 | # Push Notifications 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | **Delivery** 14 | 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`. 15 | 16 | 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. 17 | 18 | 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. 19 | 20 | ## Rpush Implementation 21 | 22 | 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. 23 | 24 | More details about how this works [here](https://github.com/rpush/rpush/wiki/Using-Redis). 25 | -------------------------------------------------------------------------------- /docs/backend/roles.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 13 3 | --- 4 | 5 | # Roles 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## What is a role? 14 | 15 | If authorization is about who has permission to be allowed to do what you want 16 | to do, then Roles are common patterns of authorization across users - reducing 17 | the administrative overhead. 18 | 19 | ## Why do I need to know about roles? 20 | 21 | Some bugs can only be seen for users with specific roles. You will need to 22 | change the role to reproduce a problem. 23 | 24 | ## How do we implement roles in Forem? 25 | 26 | Roles are implemented in this application using [Rolify][1]. The list of roles 27 | can be found in [app/models/role.rb][2] and you can search for [has_role in the 28 | codebase][3] to find which pages need which roles. 29 | 30 | A new user starts without any roles, and there is no administrative way of 31 | adding roles to users yet. To assign a user a role you will have to run commands 32 | at the console. 33 | 34 | ## Example of adding permissions to a user 35 | 36 | - open the Rails console. 37 | 38 | ```shell 39 | rails console 40 | ``` 41 | 42 | - after verifying the user `test_user_name` is missing the `trusted` role we 43 | proceed to add it and then verify the role has been added: 44 | 45 | ```ruby 46 | > user = User.find_by(username: "test_user_name") 47 | > user.has_role?(:trusted) 48 | => false 49 | 50 | > user.add_role(:trusted) 51 | => # 55 | 56 | > user.has_role?(:trusted) 57 | => true 58 | ``` 59 | 60 | Another common requirement is changing to the administrative role, and an 61 | example of this is found [on the admin page][5]. 62 | 63 | ## Verification 64 | 65 | A more complex query to list all the users and their roles: 66 | 67 | ```ruby 68 | User.joins(:roles).order(:id).group(:id).pluck(:id, :username, Arel.sql("array_agg(roles.name)")) 69 | ``` 70 | 71 | ## Further Reading 72 | 73 | 1. [Rolify README.md][1] 74 | 2. [What is the purpose of Rolify?][4] 75 | 3. [Admin][5] 76 | 77 | [1]: https://github.com/RolifyCommunity/rolify 78 | [2]: https://github.com/forem/forem/blob/main/app/models/role.rb 79 | [3]: https://github.com/forem/forem/search?q=has_role&unscoped_q=has_role 80 | -------------------------------------------------------------------------------- /docs/backend/scheduled-jobs.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 16 3 | --- 4 | 5 | # Scheduled Jobs 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | As in the [Technical Overview](../technical-overview/stack), We use 14 | [Heroku Scheduler](https://devcenter.heroku.com/articles/scheduler) for 15 | scheduled jobs. As the name suggests, this is for regularly recurring tasks that 16 | need to be run every day, week, month, year, decade, and century. 17 | 18 | Tasks are implemented in `forem/lib/tasks/fetch.rake`, typically in the form of: 19 | 20 | ``` 21 | task some_unique_task_name:, [optional_arg] => :environment do |optional_arg| 22 | ... do your thing here ... 23 | end 24 | ``` 25 | 26 | You can explore the 27 | [official Heroku documentation](https://devcenter.heroku.com/articles/scheduler#defining-tasks) 28 | for defining tasks and read through tasks we have implemented for busting cache, 29 | awarding badges, and more. 30 | 31 | In your Pull Request, communicate with a Forem Core Team Member to discuss at 32 | what frequency and ensure your task is scheduled on Heroku once your code is 33 | reviewed, approved, and merged. 34 | -------------------------------------------------------------------------------- /docs/backend/service-objects.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 20 3 | --- 4 | 5 | # Service Objects 6 | 7 | Service objects are Plain Old Ruby Objects (POROs) which encapsulate a whole 8 | business process/user interaction. We rely on these objects in our codebase to remove complexity from the Models and Controller. 9 | 10 | Our services are located in `app/services` with the corresponding specs in 11 | `spec/services`. 12 | 13 | ## Naming Conventions for Service Objects 14 | 15 | - We prefer to namespace our Service Objects especially when we potentially see the addition of more services under that namespace. This is adviseable rather than cluttering the root folder. 16 | - We namespace services in a plural form to avoid running into problems with Rails naming conventions. For example `Reactions::` instead of `Reaction::`. 17 | - To distinguish services from models we often follow the VerbNoun naming convention for services, like `HomePage::FetchArticles` instead of `HomePage::ArticleRetrieval`. However, sometimes the naming of the service may not fit within this convention and if you can rationalize about why you should use a different name then we will leave it to your discretion. 18 | - We encourage you to keep the namespaces to be the same across services, workers etc. 19 | 20 | ## .call Pattern 21 | 22 | We use the `.call` pattern when creating services. This makes it clear and consistent to identify where our primary logic for the service lies. 23 | 24 | For example: We'd prefer `result = ProfileFields::Add.call` vs `result = ProfileFields::Add.result`. 25 | 26 | However, as with the naming conventions if you can rationalize about why you should use a custom method name then we will leave it to your discretion. 27 | 28 | # Skeleton of a Service Object 29 | 30 | Most Services Objects will contain the following skeleton: 31 | 32 | ```ruby 33 | class ImportUsers 34 | def self.call(arg1) 35 | new(arg1).call 36 | end 37 | 38 | def initialize(arg1) 39 | @arg1 = arg1 40 | end 41 | 42 | def call 43 | # import code goes here 44 | end 45 | end 46 | ``` 47 | 48 | ## Generating Service Objects 49 | 50 | To make our services more consistent we use a custom Rails generator. Some usage 51 | examples: 52 | 53 | ### Generate a non-namespaced service 54 | 55 | `$ rails generate service DoTheThing` 56 | 57 | ```ruby 58 | # app/services/do_the_thing.rb 59 | class DoTheThing 60 | def self.call 61 | new.call 62 | end 63 | 64 | def call 65 | end 66 | end 67 | ``` 68 | 69 | ```ruby 70 | # spec/services/do_the_thing_spec.rb 71 | require "rails_helper" 72 | 73 | RSpec.describe DoTheThing, type: :service do 74 | pending "add some examples to (or delete) #{__FILE__}" 75 | end 76 | ``` 77 | 78 | If there are arguments for your service you can generate it as follows: 79 | 80 | ```$ rails generate service DoTheThing arg1 arg2``` 81 | 82 | ### Generate a namespaced service 83 | 84 | `$ rails generate service things/dothem arg1 arg2` 85 | 86 | ```ruby 87 | # app/services/things/do_them.rb 88 | class Things::DoThem 89 | def self.call(arg1, arg2) 90 | new(arg1, arg2).call 91 | end 92 | 93 | def initialize(arg1, arg2) 94 | @arg1 = arg1 95 | @arg2 = arg2 96 | end 97 | 98 | def call 99 | end 100 | end 101 | ``` 102 | 103 | ```ruby 104 | # spec/services/things/do_them_spec.rb 105 | require "rails_helper" 106 | 107 | RSpec.describe Things::DoThem, type: :service do 108 | pending "add some examples to (or delete) #{__FILE__}" 109 | end 110 | ``` -------------------------------------------------------------------------------- /docs/backend/tracking.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 19 3 | --- 4 | 5 | # Tracking 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## Visits & Events 14 | 15 | For first-party analytics, we use the 16 | [`ahoy_matey` gem](https://github.com/ankane/ahoy), which tracks visits and 17 | events. 18 | 19 | We intentionally choose to limit what user data we track and persist, and have 20 | opted to follow the GDPR compliance standards 21 | [set by Ahoy](https://github.com/ankane/ahoy#gdpr-compliance-1). By default, we 22 | have configured the Ahoy library to mask IP addresses, disable geocode tracking, 23 | and not track user cookies. 24 | 25 | ### Visits 26 | 27 | Ahoy creates an `Ahoy::Visit` record for each visit that it tracks. 28 | 29 | By default, we have turned off visit tracking in the `ApplicationController`: 30 | 31 | ```ruby 32 | skip_before_action :track_ahoy_visit 33 | ``` 34 | 35 | We currently only create visits on the server-side when they are required to be 36 | created by events. Visits can be re-enabled for specific controller actions if 37 | necessary, but this should be done so _with explicit care_. 38 | 39 | We do not collect any personal user data when tracking visits. Our collected 40 | data is limited to the user's `id`. Each user has a unique `visitor_token`, 41 | while each visit to the site is marked with a unique `visit_token`. 42 | 43 | ### Events 44 | 45 | Ahoy creates an `Ahoy::Event` record for each event that it tracks. If no visit 46 | is recorded for a user when an event is tracked, Ahoy will simultaneously create 47 | an `Ahoy::Visit` for the event being tracked. 48 | 49 | Events can be tracked in a controller action on the backend, or with JavaScript 50 | on the frontend. Learn more about tracking events with JavaScript in our 51 | [frontend tracking guide](../frontend/tracking). 52 | 53 | When an event is tracked, it should include a `name` and a `properties` hash. 54 | When adding new events, be sure that the name is unique per-event. The 55 | properties will help you differentiate between events. 56 | 57 | In order to track a specific event in a controller, use the `ahoy.track` call: 58 | 59 | ```ruby 60 | class YourController < ApplicationController 61 | after_action :track_my_action 62 | 63 | protected 64 | 65 | def track_my_action 66 | ahoy.track "A specific description of your event", request.path_parameters 67 | end 68 | end 69 | ``` 70 | 71 | Event tracking can be enabled for specific controller actions, but should be 72 | done so _with explicit care_. 73 | 74 | ## Messages 75 | 76 | For email analytics, we use the 77 | [`ahoy_messages` gem](https://github.com/ankane/ahoy_email), which tracks a 78 | history of email messages sent to users. 79 | 80 | Ahoy creates an `Ahoy::Message` record for each email sent by default, but can 81 | be disabled on a per-mailer basis. 82 | -------------------------------------------------------------------------------- /docs/contributing-guide/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Contributing Guide", 3 | "position": 5 4 | } -------------------------------------------------------------------------------- /docs/contributing-guide/api.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Contributing to the API Specification Docs 6 | 7 | The v0 and v1 APIs are described with the [OpenAPI 3 specification](https://spec.openapis.org/oas/v3.0.3). 8 | 9 | Swagger.io has 10 | [great docs](https://swagger.io/docs/specification/basic-structure/) that are 11 | helpful to understand the specification better. 12 | 13 | ## Where you can find the spec file 14 | 15 | We auto-generate the v0 documentation from `api_v0.yml`, and v1 documentation from the `api_v1.json`, both 16 | located within the `/docs` directory. We use [ReDoc](https://github.com/Redocly/redoc) to turn the OpenAPI 17 | 3 format into a readable and searchable HTML documentation. 18 | 19 | ## Updating API docs 20 | 21 | There should be very infrequent reasons to update the v0 documentation. If must make changes to the v0 API docs, 22 | make sure to bump the version at the top of `api_v0.yml`, in `info.version`. 23 | 24 | v1 API documentation is generated from the file `api_v1.json` which is generated via the [Forem source code](https://github.com/forem/forem) via the [rswag gem](https://github.com/rswag/rswag). To add to the documentation: create or add to an existing 25 | documentation test under `spec/requests/api/v1/docs`. Run the documentation suite via the command 26 | 27 | ```shell 28 | SWAGGER_DRY_RUN=0 RAILS_ENV=test rails rswag PATTERN="spec/requests/api/v1/docs/*_spec.rb" 29 | ``` 30 | 31 | An OpenAPI 3 spec file will be generated at `swagger/v1/api_v1.json`. Copy this file to the root of the forem-docs project, then refer to the 32 | [forem-docs readme](https://github.com/forem/forem-docs/blob/main/README.md) to rebuild the documentation site. 33 | 34 | ## Running and editing the docs locally 35 | 36 | If you want to browse the documentation locally you can use: 37 | 38 | ```shell 39 | yarn api-docs:serve 40 | ``` 41 | 42 | This will let you browse the auto-generated version of the doc locally, and it 43 | will reload the documentation after every change of the specification file. 44 | 45 | ## Linting and validation 46 | 47 | We use [spectral](https://github.com/stoplightio/spectral) and 48 | [ibm-openapi-validator](https://github.com/IBM/openapi-validator) to validate 49 | the spec file. The validation is performed as a `pre-commit` hook. 50 | 51 | You can also manually validate the document, running: 52 | 53 | ```shell 54 | yarn api-docs:lint 55 | ``` 56 | 57 | If you have Visual Studio Code, we suggest you install the following extensions 58 | that enable validation and navigation within the spec file: 59 | 60 | - [OpenAPI (Swagger) editor](https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi) 61 | - [openapi-designer live preview](https://marketplace.visualstudio.com/items?itemName=philosowaffle.openapi-designer) 62 | -------------------------------------------------------------------------------- /docs/contributing-guide/docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Contributing to the Docs 6 | 7 | **Contributing to Forem's developer documentation** 8 | 9 | Contributions to the documentation are always appreciated! Thank you for making 10 | an effort to improve the developer experience of contributing to the DEV 11 | project. 12 | 13 | # Running the documentation locally 14 | 15 | Like Forem, this site is open source and the code is [hosted on GitHub][docs]. 16 | If you find any incorrect information, or a even a typo, we'd love to see a pull 17 | request. Follow these steps to get the documentation site running locally. 18 | 19 | Forem's documentation is built with [Docusaurus][docusaurus]. 20 | 21 | Once installed, you should run `yarn start` in the root of the `forem-docs`. 22 | 23 | ```shell 24 | yarn start 25 | ``` 26 | 27 | This will start a server where you can browse the documentation: 28 | 29 | 30 | If you add new pages or rename existing pages, those changes will be to take 31 | effect shortly, so you don't need to restart the server. 32 | 33 | Since our documentation is built on Docusaurus, which is built on Netlify, you 34 | can use the generated deploy preview link to check out your documentation 35 | changes. 36 | Once you make a PR, click on "Show all checks" and find the "deploy/netlify" 37 | row. If your deploy preview is ready, you can click on "Details" to see the 38 | preview. Please note that the deploy preview only reflects any documentation 39 | changes you make (and not any changes elsewhere in the app). 40 | 41 | # Useful links 42 | 43 | The docs are a collection of [Markdown files][markdown] that also utilize 44 | [FrontMatter][frontmatter]. 45 | 46 | For more information on how to use Docusaurus, read the [Docusaurus 47 | docs][docusaurusdocs]. 48 | 49 | # Regarding language & style 50 | 51 | We ask that you avoid trivializing terms when contributing to documentation. 52 | This includes words like "just", "simply", "easy", "obvious", and 53 | "straightforward". You can learn more about why we want to avoid this kind of 54 | language in [this blog post](https://jessitron.com/2020/06/26/just-dont). 55 | 56 | Generally speaking, the documentation hosted on this site is informal. There is 57 | no need to make things more complicated by writing these articles like a 58 | textbooks. 59 | 60 | However, it's expected that contributions to these documents are reasonably 61 | structured and mostly free of spelling and grammar errors. For this reason, if 62 | you submit a PR you might be asked to make changes before your PR is merged. 63 | 64 | Prettier is used to autowrap lines in these files to 80 characters. Using 80 65 | characters per line allows us to retain a more specific git history over time. 66 | If lines are not wrapped, changing a comma in a paragraph would attribute the 67 | entire paragraph to one commit. By line wrapping we are helping git to correctly 68 | attribute smaller changes to their commits. This keeps information from getting 69 | lost over time. 70 | 71 | For more information on effective technical writing, check out 72 | [writethedocs.org][writethedocs]. 73 | 74 | [docs]: https://github.com/forem/forem-docs 75 | [docusaurus]: https://docusaurus.io/ 76 | [markdown]: https://en.wikipedia.org/wiki/Markdown 77 | [frontmatter]: https://jekyllrb.com/docs/front-matter/ 78 | [docusaurusdocs]: https://docusaurus.io/docs 79 | [writethedocs]: https://www.writethedocs.org/guide/ 80 | -------------------------------------------------------------------------------- /docs/creators/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Forem Creators Guide", 3 | "position": 15 4 | } -------------------------------------------------------------------------------- /docs/creators/configure-forem.md: -------------------------------------------------------------------------------- 1 | # Configuring Forem 2 | 3 | **Basic Site Configuration Guide for Forem** 4 | 5 | ## Overview 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | As a Forem admin, one of the first steps of managing your site will be to tailor 14 | your content, branding and other important details based on your community 15 | goals. This is made simple by the configuration page which can be found at 16 | https://your.forem.url/admin/customization/config. 17 | 18 | _We advise that you first complete the minimum set up before sending out your 19 | Forem link to your community._ 20 | 21 | ## Complete your Set Up, Configure your Forem 22 | 23 | Once your Forem instance is set up for the first time, you will most likely see 24 | the following banner: 25 | 26 | ![Banner prompting for configuring mandatory settings](/img/docs/creators/creators-banner.png) 27 | 28 | This banner indicates that the Forem configuration process hasn't been completed 29 | yet. 30 | 31 | When you click on the link on banner, it will take you to the configuration page 32 | (i.e. https://your.forem.url/admin/customization/config). 33 | 34 | On this page you will see that the Get Started section is expanded. It contains 35 | all the mandatory fields that need to be filled out in order for the site to be 36 | in a usable state. Once it is filled out and submitted, the banner will then 37 | disappear. 38 | 39 | ## Access and Permissions 40 | 41 | The following permissions are required to be able to view and/or edit the config 42 | page: 43 | 44 | #### `Role: super_admin` 45 | 46 | When providing this role to a user they will be able to access the config page. 47 | However, this page will be a read only view for them. They will see the 48 | following: 49 | 50 | ![Super Admin Permissions Role Provided](/img/docs/creators/creators-permissions.png) 51 | 52 | #### `Role: single_resource_admin` 53 | 54 | When providing this role to a user they will be able to access the config page, 55 | and they will be able to edit the config variables. 56 | 57 | ![Super Resource Admin Role Provided](/img/docs/creators/creators-role-single.png) 58 | 59 | The first admin of your Forem will be given the highest permissions. Thereafter, 60 | they can provide the necessary permissions to subsequent users via the 61 | https://your.forem.url/admin/permissions. 62 | 63 | ## The Configuration Sections 64 | 65 | Currently, the configuration page is split into 3 sections. They are as follows: 66 | 67 | - A **Get Started section** that contains all required fields. These are the 68 | fields that are required to be filled out, in order to get your Forem in a 69 | usable state. 70 | - An **All Settings section** that contains all the possible variables 71 | that you can configure on the site. This section is broken down into sub 72 | sections, whereby each subsection will contain a description of what it does, 73 | and then list the fields that are available for configuration. Each field will 74 | also contain a concise description of what it is used for. 75 | - An **Environment Variables section** that provides a read-only view of the 76 | environment variables that are available to be set on the server. If your 77 | instance is hosted by Forem, please get in touch with customer support to 78 | change any of these variables. 79 | 80 | ![The Settings Sections](/img/docs/creators/creators-settings.png) 81 | 82 | All required fields are marked as such. In addition, you will notice that we 83 | have set some defaults for certain fields, you may amend them as you see 84 | relevant for your Forem. 85 | 86 | ## Updating your configurations 87 | 88 | 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 89 | of the section where you will verify that you would like to make the change by 90 | typing the following sentence: 91 | 92 | ``` 93 | My username is and this action is 100% safe and appropriate. 94 | ``` 95 | 96 | You will now see your updated values and the changes on your site will be in 97 | effect. 98 | 99 | ![Submit your data](/img/docs/creators/creators-data.png) 100 | -------------------------------------------------------------------------------- /docs/design-guide/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Design Guide", 3 | "position": 8 4 | } -------------------------------------------------------------------------------- /docs/design-guide/branding.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Branding Guidelines 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## Forem Theme 14 | 15 | Forem's design inspiration is meant to be a throwback. In personality and 16 | design, it is ATARI inspired. Magazines like PAPER are also an inspiration in 17 | terms of being a 18 | [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). 19 | 20 | We also think in terms of how people use the Forem brand for their own purposes. 21 | Profiles are very much public-facing records of what people have put out into 22 | the world. `dev.to/{username}` is your developer identity. 23 | 24 | Imagine a developer's face with the DEV heading above them. If we existed in the 25 | 80's, we would have been "DEV MAGAZINE," with the exact same branding. 26 | 27 | ## Branding Rules 28 | 29 | - [Joystick](http://www.pixelsagas.com/?download=joystick) should be used for 30 | logos, not for headlines/phrases. 31 | - Black and white is our default logo, but the logo can be drawn in any color. 32 | - Forem/DEV is "old school cool." 33 | - We use bold shadows (no gradient) for boxes. 34 | - We have a minimal approach to importing dependencies, so few if any custom 35 | fonts on-site for that reason. 36 | - Refer to the `variables.scss` 37 | [file in our main repo](https://github.com/forem/forem/blob/main/app/assets/stylesheets/variables.scss) 38 | to reference commonly-used colors and fonts. 39 | 40 | ## Design License Info 41 | 42 | - We use the [Joystick](http://www.pixelsagas.com/?download=joystick) font by 43 | Neale Davidson and [Pixel Sagas](http://www.pixelsagas.com/) for the DEV Logo. 44 | We have a commercial license. 45 | - We use [EmojiOne](https://www.emojione.com/) icons from JoyPixels. We have a 46 | commercial license. 47 | -------------------------------------------------------------------------------- /docs/design-guide/language.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Language 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## Human Language 14 | 15 | 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. 16 | 17 | 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. 18 | 19 | ## Post 20 | 21 | 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. 22 | 23 | ## Comment 24 | 25 | A single comment is the unit of follow up dialog that lives underneath a post. Comments have some associated terminology. 26 | 27 | - A Discussion is the collection of all comments under a post. Discussion is a word that implies a certain civility. 28 | - A thread is a single line of comments and their reply comments within a full discussion. 29 | - A comment within a thread may be thought of as a "reply" contextually, but it is still a comment. 30 | 31 | ## Reaction 32 | 33 | 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. 34 | 35 | ## Tag 36 | 37 | A tag is the main unit of organized content on a Forem. 38 | 39 | ## User 40 | 41 | 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. 42 | 43 | ## Listing 44 | 45 | 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. 46 | -------------------------------------------------------------------------------- /docs/design-guide/theming.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Theming Guidelines 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Forem supports different themes, such as Default, Night, Pink. 14 | 15 | You can switch the theme at in the "Style 16 | Customization" section. 17 | 18 | These themes are powered by 19 | [CSS custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties), 20 | loaded at runtime by JavaScript via `layouts/_user_config.html.erb`. 21 | 22 | An example of how it works: 23 | 24 | ```javascript 25 | try { 26 | const bodyClass = localStorage.getItem('config_body_class'); 27 | 28 | if (bodyClass) { 29 | document.body.className = bodyClass; 30 | 31 | if (bodyClass.includes('night-theme')) { 32 | document.getElementById('body-styles').innerHTML = ''; 33 | } else if (bodyClass.includes('ten-x-hacker-theme')) { 34 | document.getElementById('body-styles').innerHTML = '' 35 | } else if (bodyClass.includes('pink-theme')) { 36 | document.getElementById('body-styles').innerHTML = '' 37 | } else if(bodyClass.includes('minimal-light-theme')) { 38 | document.getElementById('body-styles').innerHTML = '' 39 | } 40 | ... 41 | ``` 42 | 43 | Within SCSS files located at `app/assets/stylesheets` you can use `var()` 44 | statements, which will call these CSS custom properties. If a CSS custom 45 | property is not defined `var()` will fall back on the second parameter provided, 46 | which can be a SCSS variable defined in `app/assets/stylesheets/variables.scss`. 47 | 48 | An example of how to use `var()`: 49 | 50 | ```scss 51 | div { 52 | color: var(--theme-color, $black); 53 | } 54 | ``` 55 | 56 | Note that fallback values aren't used to fix the browser compatibility. If the 57 | browser doesn't support CSS custom Properties, the fallback value won't help. To 58 | prevent this issue on older browsers, we need to write our styles like this: 59 | 60 | ```scss 61 | div { 62 | color: $black; 63 | color: var(--theme-color, $black); 64 | } 65 | ``` 66 | 67 | This can be too much work and browser fallback can be forgotten. For a better 68 | developer experience, you should use the two mixins defined in 69 | `app/assets/stylesheets/_mixins.scss` called `themeable` and 70 | `themeable-important`. They take three arguments. The first argument is the CSS 71 | property like `color` or `background`, the second argument is the CSS custom 72 | property name (without `--`) like `theme-color`, and the third argument is the 73 | CSS value, i.e., `$black` or `white`. 74 | 75 | Make sure to import the mixin in your SCSS file and use it like this: 76 | 77 | ```scss 78 | div { 79 | @include themeable(color, theme-color, $black); 80 | } 81 | ``` 82 | 83 | `themeable-important` is used when a CSS variable requires `!important` as a 84 | postfix of the CSS property's value where the CSS variable is being used. You 85 | can use it the same way you would use `themeable` mixin, but you should avoid 86 | `!important` if possible. 87 | 88 | ## Other user config 89 | 90 | In addition to themes, users can also directly configure their preferred fonts 91 | and their nav bar preferences. The implementation of these is similar to themes. 92 | -------------------------------------------------------------------------------- /docs/frontend/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Frontend Guide", 3 | "position": 7 4 | } -------------------------------------------------------------------------------- /docs/frontend/accessibility.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Accessibility 6 | 7 | To make Forem the most inclusive community platform around, accessibility should 8 | be considered to enable people with disabilities to create and consume content. 9 | 10 | ## The Basics 11 | 12 | Forem UI changes should consider accessibility wherever possible. Common issues 13 | to watch out for in frontend code: 14 | 15 | - [Adequate color contrast](https://webaim.org/articles/contrast/evaluating) 16 | - [Semantic structure and headings](https://webaim.org/techniques/semanticstructure/) 17 | - [Alternative text for images](https://webaim.org/techniques/alttext/) 18 | - [Unique button and link text](https://webaim.org/techniques/hypertext/link_text) 19 | - [Accessible forms with labels](https://webaim.org/techniques/forms/) 20 | - [Visible keyboard focus styles](https://www.washington.edu/accessibility/checklist/focus/) 21 | 22 | ## More Advanced Things 23 | 24 | If you're working on something JavaScript-heavy or animated, there are a few 25 | additional considerations for accessibility: 26 | 27 | - [Forem Accessibility Tests](../tests/other-focus-areas/accessibility-tests.md) 28 | - [Intro to ARIA](https://webaim.org/techniques/aria/) 29 | - [Handle focus for client-side interactions](https://dev.to/robdodson/managing-focus-64l) 30 | - [Reducing motion with CSS media queries](https://css-tricks.com/introduction-reduced-motion-media-query/) 31 | - [Linting with eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y) 32 | - [Testing with Jest-axe](https://dev.to/bdougieyo/accessibility-testing-in-react-with-jest-axe-l7k) 33 | 34 | ## Accessibility Testing 35 | 36 | See a list of testing steps to follow during development or for a Pull Request 37 | review on the 38 | [Forem Accessibility Testing Docs](../tests/other-focus-areas/accessibility-tests.md). 39 | 40 | ## Resources 41 | 42 | There's a wealth of information out there to learn about digital accessibility! 43 | Here are some resources: 44 | 45 | - [W3C's Web Accessibility Initiative](https://www.w3.org/WAI/) 46 | - [Web Content Accessibility Guidelines](https://www.w3.org/TR/WCAG21/) 47 | - [ARIA Authoring Practices](https://www.w3.org/TR/wai-aria-practices-1.1/) 48 | - [WebAIM](http://webaim.org/) 49 | - [A11y Project](https://a11yproject.com) 50 | - [Deque University](https://dequeuniversity.com/) 51 | - [React Accessibility Docs](https://reactjs.org/docs/accessibility.html) (most 52 | will apply to Preact) 53 | - [The Importance of Manual Accessibility Testing](https://www.smashingmagazine.com/2018/09/importance-manual-accessibility-testing/) 54 | - [Accessibility Insights extension](https://accessibilityinsights.io/) 55 | -------------------------------------------------------------------------------- /docs/frontend/debugging.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Debugging 6 | 7 | ## In Browser 8 | 9 | Browsers ship with their own developer tools. These are amazing tools to help 10 | you debug your web application. Consider learning how to use them. 11 | 12 | - [Chrome Developer Tools](https://developers.google.com/web/tools/chrome-devtools) 13 | - [Firefox Developer Tools](https://developer.mozilla.org/en-US/docs/Tools) 14 | - [Safari Developer Tools](https://support.apple.com/en-ca/guide/safari/sfri20948/mac) 15 | 16 | ## Visual Studio Code 17 | 18 | [Visual Studio Code](https://code.visualstudio.com) (VS Code) is a popular 19 | editor that allows you to debug many languages including JavaScript. Thanks to 20 | remote debugging technologies, we can debug our frontend code in VS Code. When 21 | you clone the Forem repository and open the project in VS Code, you will be 22 | prompted to install recommended extensions which include the 23 | [Chrome Debugger](https://code.visualstudio.com/blogs/2016/02/23/introducing-chrome-debugger-for-vs-code) 24 | and the 25 | [Edge Debugger](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-edge). 26 | 27 | Setup: 28 | 29 | - Refer to the respective debugger extension documentation above to ensure that 30 | your browser is running with remote debugging enabled. 31 | - Once you have your local installation of Forem running, you can attach to 32 | either the Chrome or Edge Debugger. 33 | 34 | ![Launch menu for debugger in VS Code](/img/docs/frontend/debugger.png) 35 | 36 | - From there you can do all the usual stuff that you would do while debugging 37 | JavaScript in the browser: setting breakpoints, setting 38 | [logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints), 39 | watches etc. 40 | 41 | ## Where is My Editor Debug Configuration? 42 | 43 | If you do not see your editor here, consider contributing to the documentation. 44 | 😉 45 | 46 | ## Preact Developer Tools 47 | 48 | Preact has their 49 | [own developer tools](https://preactjs.github.io/preact-devtools/) in the form 50 | of a browser extension. The Forem codebase is configured out of the box to 51 | support the Preact Devtools. 52 | -------------------------------------------------------------------------------- /docs/frontend/dynamic-imports.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Dynamic Imports 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | [Dynamic imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import/#Dynamic_Imports) 14 | are supported in all major browsers except for Edge 15 | ([EdgeHTML](https://en.wikipedia.org/wiki/EdgeHTML) version) and Internet 16 | Explorer which are both unsupported browsers for Forem. They allow you to import 17 | JavaScript dynamically instead of statically. Why is this important? 18 | Performance. 19 | 20 | Should you use them everywhere? No. They are a great tool when you need to load 21 | a JavaScript module on the fly for functionality that is not needed immediately 22 | for the page to be usable. 23 | 24 | Here are a couple of examples of dynamic import usage on Forem: 25 | 26 | - The 27 | [Onboarding flow](https://github.com/forem/forem/blob/0633d85b6b0e083bb7b21b11642b2b17d3fe9de6/app/javascript/packs/Onboarding.jsx#L21). 28 | - In 29 | [homepage](https://github.com/forem/forem/blob/0633d85b6b0e083bb7b21b11642b2b17d3fe9de6/app/javascript/packs/homePage.jsx#L59) 30 | (followed tags). 31 | 32 | Forem uses [webpacker](javascript-approaches/packs.md), so what webpack will do is 33 | create separate bundles for code that is dynamically imported. So not only do we 34 | end up loading code only when we need it, we also end up with smaller bundle 35 | sizes in the frontend. 36 | 37 | For a great deep dive into dynamic imports, there is a great article from 38 | community member [@goenning](https://dev.to/goenning) about dynamic import 39 | usage, 40 | [How we reduced our initial JS/CSS size by 67%](https://dev.to/goenning/how-we-reduced-our-initial-jscss-size-by-67-3ac0). 41 | -------------------------------------------------------------------------------- /docs/frontend/instant-click.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # InstantClick 6 | 7 | Like the [InstantClick](http://instantclick.io/) tag line says, “InstantClick is 8 | a JavaScript library that dramatically speeds up your website, making navigation 9 | effectively instant in most cases.”. 10 | 11 | [Read more about InstantClick](https://developers.forem.com/frontend/instant-click) in the developer documentation. 12 | 13 | The way it works is if a user hovers over a hyperlink, chances are their 14 | intentions are to click on it. InstantClick will start prefetching the page 15 | while a user is hovering over a hyperlink, so that by the time they do click on 16 | it, it's instantaneous. On mobile devices, preloading starts on 17 | [touchstart](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchstart_event). 18 | 19 | Aside from prefetching pages, InstantClick also allows you to customize what 20 | happens when an InstantClick page changes. 21 | 22 | ```javascript 23 | // Found in https://github.com/forem/forem/blob/main/app/javascript/packs/githubRepos.jsx#L11) 24 | window.InstantClick.on("change", () => { 25 | loadElement(); 26 | }); 27 | ``` 28 | 29 | You can also decide whether or not to reevaluate a script in an InstantClick 30 | loaded page via the `data-no-instant` attribute. 31 | 32 | ```javascript 33 | // Found in https://github.com/forem/forem/blob/main/app/assets/javascripts/utilities/buildCommentHTML.js.erb#L80 34 | function actions(comment) { 35 | if (comment.newly_created) { 36 | return '
\ 37 | \ 41 | REPLY\ 42 |
'; 43 | } else { 44 | ... 45 | ``` 46 | 47 | For more information on this, see the 48 | [Events and script re-evaluation in InstantClick](http://instantclick.io/scripts) 49 | documentation. 50 | 51 | We have [modified InstantClick for our needs](https://github.com/forem/forem/blob/main/app/assets/javascripts/base.js.erb#L13). 52 | -------------------------------------------------------------------------------- /docs/frontend/javascript-approaches/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "JavaScript Approaches", 3 | "position": 2 4 | } -------------------------------------------------------------------------------- /docs/frontend/javascript-approaches/initializers.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Initializers & the asset pipeline 6 | 7 | :::important 8 | 9 | When refactoring or adding new functionality that lives in the asset pipeline, strongly consider moving it to Packs and Webpacker. 10 | 11 | ::: 12 | 13 | The asset pipeline is where we have the "legacy" JavaScript. It's legacy in the sense that it's not EcmaScript Modules (ESM). It's a bunch of JS files concatenated together being served using [Sprockets](https://github.com/rails/sprockets-rails) which packages static assets. They are available globally. 14 | 15 | 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. 16 | 17 | `app/assets/javascripts/application.js` contains the manifest JavaScript file which is included globally in the primary template, `app/views/layouts/application.html.erb`. 18 | 19 | `application.js` automatically includes all JS files via the statement: 20 | 21 | ``` 22 | //= require_tree . 23 | ``` 24 | 25 | One JS file in particular, `app/assets/javascripts/initializePage.js`, bootstraps the majority of the functionality and calls its initializers on every page. You will notice, within this file, that major sections of the websites are bootstrapped, for example: 26 | 27 | ``` 28 | initializeBaseTracking(); 29 | initializeCommentsPage(); 30 | initEditorResize(); 31 | initLeaveEditorWarning(); 32 | initializeArticleReactions(); 33 | initNotifications(); 34 | initializeSplitTestTracking(); 35 | ``` 36 | 37 | All the "initializers" are in `/app/assets/javascripts/initializers`. 38 | 39 | 40 | Another example of an initializer is `initializeArticleReactions.js` which fetches reaction counts and adds click listeners. It adds interactivity to buttons in `app/views/articles/_reaction_button.html.erb`. 41 | 42 | It's useful to note that some html.erb views reference function names directly, e.g. `handleCommentSubmit` in `initializeCommentsPage.js.erb.` 43 | 44 | 45 | `base.js.erb` concatenates all the files together and gives us `base.js` in production/development environments. -------------------------------------------------------------------------------- /docs/frontend/javascript-approaches/inline-scripts.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Inline Scripts 6 | 7 | Inline Scripts are less frequently used. Several have been recently refactored to Packs and we advise that you use packs where possible. 8 | 9 | Currently these inline scripts are mostly used to initialize liquid tag behaviours e.g. `app/liquid_tags/runkit_tag.rb` -------------------------------------------------------------------------------- /docs/frontend/javascript-approaches/overlaps-and-challenges.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overlap and Challenges 3 | sidebar_position: 6 4 | --- 5 | 6 | # Overlaps & Challenges 7 | 8 | ## Overlaps 9 | 10 | There are some overlaps of the different JavaScript appraoches that we've outlined below: 11 | 12 | ### Webpacker code in initializers 13 | 14 | > app/javascript/packs/base.jsx 15 | 16 | e.g. Preact Modal, MentionAutocompleteTextArea 17 | 18 | - Added to the Forem namespace on the window object in `base.jsx` 19 | - Typically in an ESM world, we shouldn't require code to live on the window object, but due to the nature of the application, it's required so that non-ESM code can consume it 20 | - Can then be accessed via e.g. `window.Forem.showModal()`, e.g. see `app/assets/javascripts/initializers/initializeCommentsPage.js.erb` where we replace the vanilla textarea with the mention autocomplete one 21 | 22 | ### Stimulus JS code 23 | 24 | The following Stimulus related code will be removed once we completely remove Stimulus from our application. 25 | 26 | ### Preact in Stimulus JS 27 | e.g. `app/javascript/admin/controllers/modal_controller.js` 28 | 29 | - In some cases we want our reusable Preact components in the admin area, e.g. Modals. 30 | - We can dynamically import and render in the Stimulus controller 31 | - Currently only used for Modal and Snackbar & is a relatively new pattern for us 32 | 33 | #### Stimulus JS in packs (creator onboarding) 34 | e.g. `loadCreatorSettings` in base.jsx 35 | 36 | - Allows us to use admin controllers in the main user-facing views 37 | - Specifically added for the new creator onboarding flow 38 | 39 | ## Challenges 40 | 41 | Below are some of the challenges that we experience when working within this structure. We're currently working on improving the state of JavaScript and removing some of the challenges. 42 | 43 | - Non-initializer code needed by initializers, needing to add functions to the window.Forem namespace 44 | - Some initializer code re-runs on every page with unintended consequences 45 | - Async loading vs progressive enhancement (including testing challenges like Cypress attempting to click a button that doesn't have a handler attached yet) 46 | - Optimistic UI updates and silent failures (e.g. failed follow button click) 47 | - Duplication - e.g. the feed is loaded in multiple ways (server-side and Preact client-side), comment HTML exists in initializer JS code and html.erb code. 48 | - Multiple ways of implementing and layering interactivity on the same design system component (html.erb, Preact) -------------------------------------------------------------------------------- /docs/frontend/javascript-approaches/stimulus-js.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Stimulus JS (Legacy) 6 | 7 | We used this framework ins ome parts of the admin area, however we have made a decision to no longre use it and will be removing it in favour of using vanilla JavaScript or Preact where appropriate. 8 | 9 | - Controllers in `app/javascript/admin/controllers` 10 | - Hooks up to `html.erb` views with data attributes e.g. `data-controller="modal_controller, data-action="click->modal#onClick"` 11 | - Layers interactivity onto server-rendered views 12 | e.g. `app/javascript/admin/controllers/reaction_controller.js` used with `app/views/admin/feedback_messages/_abuse_reports.html.erb` 13 | -------------------------------------------------------------------------------- /docs/frontend/linting-formatting.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 8 3 | --- 4 | 5 | # Linting and Formatting 6 | 7 | The project uses [eslint](https://eslint.org/) with the 8 | [Prettier plugin](https://github.com/prettier/eslint-plugin-prettier). eslint 9 | handles linting, but eslint rules related to code formatting, they get handled 10 | by prettier. For the most part, out of the box rules provided by the 11 | configurations that are extended are used but there are some tweaks. 12 | 13 | Forem also has some objects that live in the global scope, e.g. InstantClick. The 14 | eslint globals section of the eslint configuration is what enables these to be 15 | reported as existing when eslint runs. 16 | 17 | ```javascript 18 | globals: { 19 | InstantClick: false, 20 | filterXSS: false, 21 | } 22 | ``` 23 | 24 | ## Husky and lint-staged 25 | 26 | The code base uses a pre-commit hook that is enabled by the 27 | [husky](https://github.com/typicode/husky) and 28 | [lint-staged](https://github.com/okonet/lint-staged) tools. The pre-commit hook 29 | runs eslint before frontend code is committed. If there are any issues that can 30 | automatically be fixed, eslint will fix them. If there are linting issues that 31 | cannot be resolved, the commit fails and the changes need to be handled 32 | manually. Prettier also runs during the pre-commit hook. 33 | -------------------------------------------------------------------------------- /docs/frontend/philosophy.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Philosophy 6 | 7 | ## Architecture 8 | 9 | Our application is server side-rendered with an["Islands Architecture"](https://jasonformat.com/islands-architecture/). 10 | 11 | The general idea of an “Islands” architecture is to render HTML pages on the server, and inject placeholders or slots around highly dynamic regions. These placeholders/slots contain the server-rendered HTML output from their corresponding widget. They denote regions that can then be "hydrated" on the client into small self-contained widgets, reusing their server-rendered initial HTML. 12 | 13 | The Islands architecture permits us to add pockets of interactivity to the application. A DOM element becomes the root of that particular interactivity, typically rendering a Preact component. 14 | 15 | ## Current JavaScript Approaches 16 | 17 | The current JavaScript approaches described below are: 18 | 19 | - [Initializers & the asset pipeline (Legacy)](javascript-approaches/initializers) 20 | - [Packs & Webpacker](javascript-approaches/packs) 21 | - [Preact](javascript-approaches/preact) 22 | - [Stimulus JS (Legacy)](javascript-approaches/stimulus-js) 23 | - [Inline Scripts](javascript-approaches/inline-scripts) 24 | 25 | You can also read more about the [Overlaps & Challenges](javascript-approaches/overlaps-and-challenges). 26 | 27 | 28 | With these options, we sometimes need to **decide what rendering mechanism to use for the frontend**, we advise the usage of: 29 | - Vanilla JavaScript where we need to do some basic interactivity, e.g. adding an event listener, minimal form validation, etc. The preferred way of incorporating the JavaScript is through pack files. 30 | - Preact for complex interactivity: search bar, autocomplete, multi-autocomplete, post subscription button, accessible colour picker. 31 | 32 | ## Examples 33 | 34 | ### Vanilla JavaScript 35 | 36 | [Bootstrapping dropdowns on a blog post page](https://github.com/forem/forem/blob/0024fe40d6ade998a216216b00f157fa7f49e1c0/app/javascript/packs/articlePage.jsx#L4) with JavaScript to make them interactive and accessible. 37 | 38 | ### Preact 39 | 40 | The [comment subscription component](https://storybook.forem.com/?path=/story/app-components-comment-subscription--subscribed) [source](https://github.com/forem/forem/blob/0024fe40d6ade998a216216b00f157fa7f49e1c0/app/javascript/CommentSubscription/CommentSubscription.jsx) is an excellent example of this. 41 | 42 | The initial potential component state is loaded, a subscribe button. If the user has not subscribed to comments of a post, the Preact component renders, but the VDOM diff determines there is nothing to do, so the server-side rendered markup remains the same. When a user subscribed to post comments and the page loads, the server-side rendered subscribe button renders, and then the comment subscription component renders the button in the unsubscribe state. 43 | 44 | -------------------------------------------------------------------------------- /docs/frontend/styles.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 9 3 | --- 4 | 5 | # Styles 6 | 7 | The majority of the CSS in the application is written in 8 | [SASS](https://sass-lang.com/). There are a few places in the code base that 9 | have style blocks in ERB templates, for inlining critical CSS (good). There are 10 | also some styles that live in ERB templates that are not critical CSS (bad). 11 | That is a bit of refactoring that needs to be done. PRs welcome! 12 | 13 | Important files when working with SASS in the project: 14 | 15 | - variables: 16 | [/app/assets/stylesheets/variables.scss](https://github.com/forem/forem/blob/main/app/assets/stylesheets/variables.scss) 17 | - mixins: 18 | [/app/assets/stylesheets/\_mixins.scss](https://github.com/forem/forem/blob/main/app/assets/stylesheets/_mixins.scss) 19 | 20 | SASS is compiled and served using 21 | [Sprockets](https://github.com/rails/sprockets-rails) which packages static 22 | assets in Rails. 23 | 24 | For more about branding, theming or design in general in regards to Forem, refer 25 | to the [Design Guide](../design-guide/branding) documentation. 26 | 27 | ## Crayons 28 | 29 | Crayons is the design system used by Forem. A 30 | [storybook](https://storybook.js.org/) listing the various elements is available 31 | at https://storybook.forem.com/ 32 | 33 | You can also run it locally with the following command: 34 | 35 | ``` 36 | $ yarn storybook 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/frontend/tips.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 10 3 | --- 4 | 5 | # Additional Info 6 | 7 | ## About query selectors 8 | 9 | JavaScript has many different query selectors, some seemingly interchangeable, 10 | for example: 11 | 12 | - `document.head` and `document.body` 13 | - `document.getElementById`, `document.getElementsByClassName` and 14 | `document.getElementsByTagName` 15 | - `document.querySelector` and `document.querySelectorAll` 16 | 17 | Knowing which to use for optimal performance depends on the situation, but a 18 | good rule of thumb is: 19 | 20 | - to access the **head** of the document, use `document.head` over 21 | `document.getElementsByTagName('head')` 22 | - to access the **body** of the document, use `document.body` over 23 | `document.getElementsByTagName('body')` 24 | - to access an element by id use `document.getElementById('id')` over 25 | `document.querySelector('#id')` 26 | - to access one element by class name use 27 | `document.getElementsByClassName('className')[0]` over 28 | `document.querySelector('.className')` 29 | - to access multiple elements by class name use 30 | `document.getElementsByClassName('className')` over 31 | `document.querySelectorAll('.className')` 32 | - to access one element by tag name use 33 | `document.getElementsByTagName('tagName')[0]` over 34 | `document.querySelector('tagName')` 35 | - to access multiple elements by tag name use 36 | `document.getElementsByTagName('tagName')` over 37 | `document.querySelectorAll('tagName')` 38 | 39 | In most cases `querySelector` and `querySelectorAll` should be used only on 40 | selectors more sophisticated than a simple id, class or tag name. 41 | 42 | ### Resources 43 | 44 | - [Forem PR 6380](https://github.com/forem/forem/issues/6380#issuecomment-592989438) 45 | - [Why is getElementsByTagName() faster than querySelectorAll()?](https://humanwhocodes.com/blog/2010/09/28/why-is-getelementsbytagname-faster-that-queryselectorall/) 46 | - [What is the difference between querySelectorAll and getElementsByTagName?](https://stackoverflow.com/a/30921553/4186181) 47 | -------------------------------------------------------------------------------- /docs/frontend/tracking.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Tracking 6 | 7 | ## Ahoy.js 8 | 9 | For first-party analytics, we use the 10 | [`ahoy.js` library](https://github.com/ankane/ahoy.js), which tracks visits and 11 | events. This library works in conjunction with the `ahoy_matey` gem, which is 12 | documented in our [backend tracking guide](../backend/tracking). 13 | 14 | ### Configuration 15 | 16 | The configuration for `ahoy.js` lives in `app/assets/javascripts/base.js.erb`. 17 | Since we do not track user cookies on the backend, we have configured 18 | `ahoy.js`'s defaults to match that on the frontend. 19 | 20 | ### Events 21 | 22 | In order to track an event, use the `ahoy.track` function: 23 | 24 | ```javascript 25 | ahoy.track(name, properties); 26 | ``` 27 | 28 | This function will send a `POST` request to the `/ahoy/events` endpoint on our 29 | backend with the `name` and `properties` of the event. The backend endpoint will 30 | also create a corresponding `Ahoy::Visit` for the event if one does not exist 31 | already. 32 | -------------------------------------------------------------------------------- /docs/getting-started/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Getting Started", 3 | "position": 4 4 | } -------------------------------------------------------------------------------- /docs/getting-started/branching.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Creating a Branch 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | **Creating a feature/bug branch** 14 | 15 | When you are working on a bug, feature, or improvement, you will need to create 16 | a branch. 17 | 18 | Branches names should be prefixed with your own GitHub username. If they have an 19 | associated issue, the issue ID should be added as a suffix. For example: 20 | 21 | ```shell 22 | git checkout -b USERNAME/that-new-feature-1234 23 | ``` 24 | 25 | or 26 | 27 | ```shell 28 | git checkout -b USERNAME/fixing-that-bug-1234 29 | ``` 30 | 31 | where `USERNAME` should be replaced by your username on GitHub and `1234` is the 32 | ID of the issue tied to your pull request. If there is no related issue, you can 33 | leave the number out. 34 | 35 | NOTE: It is ok if your branch name shows up as 36 | `USERNAME:USERNAME/fixing-that-bug-1234` when you create the pull request 37 | -------------------------------------------------------------------------------- /docs/getting-started/commiting.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Committing 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | **Committing and pre-commit hooks** 14 | 15 | ## Commit messages 16 | 17 | We encourage people to write 18 | [meaningful commit messages](https://chris.beams.io/posts/git-commit/). 19 | 20 | ## Style guide 21 | 22 | This project follows 23 | [thoughtbot's Ruby Style Guide](https://github.com/thoughtbot/guides/blob/main/ruby/.rubocop.yml), 24 | using [Rubocop](https://github.com/bbatsov/rubocop) along with 25 | [Rubocop-Rspec](https://github.com/backus/rubocop-rspec) as the code analyzer. 26 | If you have Rubocop installed with your text editor of choice, you should be up 27 | and running. 28 | 29 | For the frontend, [ESLint](https://eslint.org) and 30 | [prettier](https://github.com/prettier/prettier) are used. ESLint's recommended 31 | rules along with Preact's recommended rules are used for code-quality. 32 | Formatting is handled by prettier. If you have ESLint installed with your text 33 | editor of choice, you should be up and running. 34 | 35 | ## Husky hooks 36 | 37 | When commits are made, a git precommit hook runs via 38 | [husky](https://github.com/typicode/husky) and 39 | [lint-staged](https://github.com/okonet/lint-staged). ESLint, prettier, and 40 | Rubocop will run on your code before it's committed. If there are linting errors 41 | that can't be automatically fixed, the commit will not happen. You will need to 42 | fix the issue manually then attempt to commit again. 43 | 44 | Note: if you've already installed the [husky](https://github.com/typicode/husky) 45 | package at least once (used for pre-commit npm script), you will need to run 46 | `yarn --force` or `npm install --no-cache`. For some reason, the post-install 47 | script of husky does not run when the package is pulled from yarn or npm's 48 | cache. This is not husky specific, but rather a cached package issue. 49 | -------------------------------------------------------------------------------- /docs/getting-started/installation/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Installation", 3 | "position": 0 4 | } 5 | -------------------------------------------------------------------------------- /docs/getting-started/installation/containers.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Containers 6 | 7 | We offer two options for container-based development. 8 | 9 | 1. VS Code with Devcontainer 10 | 1. Containerized setup with `dip` gem 11 | 12 | Due to the way our we setup our `docker-compose.yml`, standard `docker-compose up` command is not support. Please checkout the "Containerized setup with `dip` gem option" 13 | 14 | If you encounter any errors with our Container setup, please kindly 15 | [report any issues](https://github.com/forem/forem/issues/new/choose)! 16 | 17 | ## VS Code with Devcontainer 18 | 19 | Forem support VS Code's Devcontainer. Devcontainer is a flexible containized development environment with all the required dependencies set up in under 15 minutes on most modern machine. 20 | 21 | ### Installation 22 | 23 | 1. Download and install Docker Desktop from the [Docker's official site](https://www.docker.com/products/docker-desktop/) 24 | 1. Download and install [VS Code](https://code.visualstudio.com/) with the [dev container extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) 25 | 1. Fork and clone our [Forem repository](https://github.com/forem/forem) into your preferred directory. For Window's WSL users, please make sure you place the Forem repository within the linux layer for optimal file loading speed. 26 | 1. Open up the repository with VS Code and you should be prompted with `Rebuild and reopen in container`. If you don't see it, you can find it in the command line palette , `Dev Containers: Rebuild and Reopen in Container`, and you're ready to go! 27 | 28 | For additional information on Devcontainer, please checkout [the offical documentation](https://code.visualstudio.com/docs/devcontainers/containers) 29 | 30 | ![image](https://github.com/forem/forem/assets/15793250/157a58a4-41f4-4114-bfce-da1380151d8c) 31 | 32 | ## Containerized setup with `dip` gem 33 | 34 | `dip` gem makes it simple to run docker commands and is the main way we use to interact with `docker-compose.yml` 35 | 36 | ### Installation 37 | 38 | 1. Fork and clone our [Forem repository](https://github.com/forem/forem) into your preferred directory. 39 | 1. Make sure you have Ruby installed and avaliable. You may use the system provided Ruby or you can install it with a version manager such as `Rbenv` or `mise`. 40 | 1. `gem install dip` 41 | 1. `dip provision` to build images and download dependencies. 42 | 1. `dip rails s` to start the server. 43 | 44 | For more commands, run `dip ls`. 45 | -------------------------------------------------------------------------------- /docs/getting-started/installation/gitpod.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Gitpod 6 | 7 | If you prefer working on a cloud IDE, you can spin up your own instance of Forem 8 | in the cloud, using [Gitpod](https://gitpod.io). 9 | 10 | 1. Fork the Forem repository. Navigate to https://github.com/forem/forem and click on the Fork button. 11 | 12 | ![Repository Fork button](/img/docs/gitpod/fork-button.png) 13 | 14 | 2. Copy the URL of your forked repository, e.g. https://github.com/forked-user/forem 15 | 3. In the browser address bar enter, e.g. https://gitpod.io/#https://github.com/forked-user/forem, where the URL after `https://gitpod.io/#` is the forked repository address. Note: If it's your first time using Gitpod, you will be redirected to their sign in/sign up flow. 16 | 4. Gitpod will load your forked version of Forem. If it's the first time doing this, it will take several minutes. All the pieces you need for your cloud environment are installing, including Ruby gems and npm packages. 17 | 18 | ![Gitpod prebuild running](/img/docs/gitpod/gitpod-prebuild.png) 19 | 20 | ![Gitpod building containers](/img/docs/gitpod/gitpod-pulling-container-image.png) 21 | 22 | 5. The IDE opens 23 | 24 | ![Gitpod IDE](/img/docs/gitpod/gitpod-ide.png) 25 | 26 | 6. Gitpod prompts to install the recommended extensions. It's not required, but it will provide a better editing experience. 27 | 28 | ![Gitpod install recommended extensions](/img/docs/gitpod/gitpod-install-vscode-extensions.png) 29 | 30 | 7. It will still take a few minutes to prepare the workspace. 31 | 8. Two terminal sessions will be running. The one named `Open Site: gp` with the prompt `Awaiting port 3000...`. 32 | 33 | ![Gitpod Open Site terminal session](/img/docs/gitpod/gitpod-open-site-terminal-session.png) 34 | 35 | This will remain until the web server is started which is running in the other terminal session named `Forem Server: bash`. 36 | 37 | ![Gitpod Forem Server terminal session](/img/docs/gitpod/gitpod-forem-server-terminal-session.png) 38 | 39 | 40 | 9. The local Forem development instance loads in the Gitpod browser preview window. 41 | 42 | ![Gitpod browser preview with the Forem development instance loaded](/img/docs/gitpod/gitpod-preview-browser.png) 43 | 44 | 10. You can now code, review, or try out the project. 45 | 46 | Additional information: 47 | 48 | - The Forem local development instance ships with one user, the admin account. The email for the user is `admin@forem.local` and the password is `password`. 49 | - More about Gitpod in this article on dev.to about [Gitpod and Forem (previously DEV)](https://dev.to/ben/spin-up-a-local-instance-of-dev-in-the-cloud-with-gitpod-it-s-incredibly-simple-pij). 50 | - Although other methods of authentication are available, only email will work. 51 | 52 | ![Gitpod Forem login page](/img/docs/gitpod/gitpod-forem-login-page.png) 53 | 54 | To use other forms of authentication, they need to be configured. Visit the [Authentication](/backend/authentication) section to configure social logins. 55 | - Instead of manually building a Gitpod URL and pasting it in the browser's address bar, consider installing the [Gitpod browser extension](https://www.gitpod.io/docs/browser-extension/). 56 | 57 | ## GitHub CLI 58 | 59 | Forem’s Gitpod configuration ships with the GitHub CLI. You can create a pull request right from the command line by running the following command: `gh pr create`. 🤯 60 | 61 | ![GitHub CLI creating a pull request](/img/docs/gitpod/gitpod-github-cli-creat-pr.png) 62 | 63 | The first time you use the GitHub CLI in Gitpod for any remote commands, like `gh pr create`, you will need to authenticate to GitHub via the GitHub CLI, i.e. `gh auth login`. 64 | 65 | ![GitHub CLI login prompt](/img/docs/gitpod/gitpod-github-cli-login.png) 66 | 67 | It is recommended to login to GitHub via SSH. 68 | 69 | ![GitHub CLI login via SSH](/img/docs/gitpod/gitpod-github-cli-login-ssh.png) 70 | 71 | Follow the instructions from the GitHub CLI to finish logging in. The browser login to Github will open in the Gitpod preview browser window and fail to load. Copy the URL from the Gitpod browser preview window and paste it in a browser tab address bar instead to complete the authentication to GitHub. 72 | 73 | For more information on how to use the GitHub CLI, see the official [GitHub CLI documentation](https://cli.github.com/manual/index). -------------------------------------------------------------------------------- /docs/getting-started/installation/imgproxy.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Imgproxy 6 | 7 | Imgproxy is a standalone server for resizing images. It is optional and you do 8 | not need it to start Forem locally. We are currently only using it in Forem 9 | Cloud. 10 | 11 | ## Installation 12 | 13 | - MacOS: install via homebrew with `brew install imgproxy`. 14 | - Windows: please install 15 | [via docker](https://docs.imgproxy.net/#/installation?id=docker). 16 | - Linux: you can either use 17 | [docker installation](https://docs.imgproxy.net/#/installation?id=docker) or 18 | [build from source](https://docs.imgproxy.net/#/installation?id=from-the-source). 19 | 20 | For more options not covered here, please take a look at the 21 | [official installation documentation](https://docs.imgproxy.net/#/installation). 22 | 23 | ## Usage 24 | 25 | 1. Generate a key/salt pair by running the following in your terminal twice. 26 | Copy those values to your `.env` in the next step 27 | 28 | ``` 29 | echo $(xxd -g 2 -l 64 -p /dev/random | tr -d '\n') 30 | ``` 31 | 32 | 1. In your `.env`, add the following. 33 | 34 | ``` 35 | export IMGPROXY_ENDPOINT='http://localhost:8080' 36 | export IMGPROXY_KEY='1b1c9aae804e070b0864f2547fba7ce8ff31bf7..........' 37 | export IMGPROXY_SALT='8c6d449d4fc2cada5bab538826cae709d2ade9f.........' 38 | ``` 39 | 40 | 1. Start the Forem app server normally. 41 | 42 | 1. Start Imgproxy in a terminal with the key and salt. 43 | 44 | ``` 45 | # If you installed via homebrew or using the binary. 46 | > IMGPROXY_KEY='your key' IMGPROXY_SALT='your salt' imgproxy 47 | 48 | # If you are using Docker or Podman. The commands are identical for both 49 | > docker run -p 8080:8080 \ 50 | -e IMGPROXY_KEY="your key" \ 51 | -e IMGPROXY_SALT="your salt" \ 52 | -it darthsim/imgproxy 53 | ``` 54 | 55 | 1. That's it :) 56 | 57 | You should verify it's working by starting the Forem app locally and see that 58 | each image is loaded properly, or run the following command while the Forem app 59 | is running: 60 | 61 | ``` 62 | > curl -I http://localhost:8080/unsafe/aHR0cDovL2xvY2Fs/aG9zdDozMDAwL2Fz/c2V0cy8xLnBuZw 63 | 64 | HTTP/1.1 200 OK 65 | Server: imgproxy 66 | X-Request-Id: GYvCGXb98JUwL3ujwpjzh 67 | Date: Tue, 27 Oct 2020 16:11:37 GMT 68 | ``` 69 | 70 | ## Sidenote 71 | 72 | - Because Imgproxy is a standalone server of its own, all image URLs given to it 73 | need to be absolute URLs. 74 | - When working with Docker or Podman on Linux, provide the host network option 75 | (`--network="host"`) so Imgproxy can properly access the localhost. 76 | -------------------------------------------------------------------------------- /docs/getting-started/installation/others.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 9 3 | --- 4 | 5 | # Other Tools 6 | 7 | ## Miscellaneous 8 | 9 | ### Foreman 10 | 11 | We use [Foreman](https://github.com/ddollar/foreman) to manage our application 12 | through `Procfile` and `Procfile.dev`. As the 13 | [documentation](https://github.com/ddollar/foreman/blob/master/README.md) points 14 | out, 15 | 16 | > Ruby users should take care _not_ to install foreman in their project's 17 | > `Gemfile`. See this 18 | > [wiki article](https://github.com/ddollar/foreman/wiki/Don't-Bundle-Foreman) 19 | > for more details. 20 | 21 | Instead install Foreman globally with the following command: 22 | 23 | ```sh 24 | $ gem install foreman 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/getting-started/installation/postgresql.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # PostgreSQL 6 | 7 | **Setup your application with PostgreSQL** 8 | 9 | Follow the instructions in the installation guide below that corresponds to your 10 | operating system: 11 | 12 | 1. macOS 13 | - [Postgres.app](https://postgresapp.com/): PostgreSQL installation as a Mac 14 | app 15 | - [Homebrew](https://brew.sh/): if you use Homebrew you can easily install 16 | PostgreSQL with `brew install postgresql` 17 | 1. Linux (Ubuntu) 18 | - [Ubuntu `18.04`](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04) 19 | - [Ubuntu `20.04`](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-20-04) 20 | 1. [Windows](https://www.postgresql.org/download/windows/) 21 | 22 | _You can find all installation options for a variety of operating systems 23 | [on the official PostgreSQL download page](https://www.postgresql.org/download/)_. 24 | 25 | ## Configuration 26 | 27 | By default, the application is configured to connect to a local database named 28 | `PracticalDeveloper_development`. If you need to specify a username and a 29 | password, you can go about it by using the environment variable `DATABASE_URL` 30 | with a connection string. 31 | 32 | The 33 | [official Rails guides](https://guides.rubyonrails.org/configuring.html#connection-preference) 34 | go into depth on how Rails merges the existing `database.yml` with the 35 | connection string. 36 | 37 | ### Setup `DATABASE_URL` in .env 38 | 39 | 1. Open your `.env` 40 | 41 | 1. Add the following: 42 | 43 | ```shell 44 | export DATABASE_URL=postgresql://USERNAME:PASSWORD@localhost 45 | ``` 46 | 47 | 1. Replace `USERNAME` with your database username, `PASSWORD` with your database 48 | password. 49 | 50 | You can find more details on connection strings in 51 | [PostgreSQL's own documentation](https://www.postgresql.org/docs/10/static/libpq-connect.html#LIBPQ-CONNSTRING). 52 | 53 | NOTE: due to how Rails merges `database.yml` and `DATABASE_URL` it's recommended 54 | not to add the database name in the connection string. This will default to your 55 | development database name also during tests, which will effectively empty the 56 | development DB each time tests are run. 57 | 58 | Because the test environment is loaded separately, if you are running tests and need to provide a postgresql url, set the `DATABASE_TEST_URL` variable in a file named .env.test.local 59 | 60 | ```shell 61 | # Optional: If your test database is in a different url, be sure to set this. 62 | export DATABASE_URL_TEST=postgresql://USERNAME:PASSWORD@localhost 63 | ``` 64 | 65 | ## Connection Pooling 66 | 67 | We use [PgBouncer](http://www.pgbouncer.org) to manage connection pooling. 68 | 69 | Database pooling creates a shared pool of connections to our database, rather 70 | than creating new connection each time. PgBouncer is a wrapper around our 71 | database connection and ensures that we only have a finite set of "cached" 72 | connections to the database. This means that our app doesn't need to actually 73 | connect to the database, it only needs to connect to the pool of connections. 74 | The number of connections to the database (the connection limit) is dependent on 75 | our 76 | [Heroku Postgres plan](https://devcenter.heroku.com/articles/heroku-postgres-plans). 77 | PgBouncer ensures that we do not exceed our plan's connection limit. 78 | -------------------------------------------------------------------------------- /docs/getting-started/pull-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Preparing a Pull Request 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | - Try to keep the pull requests small. A pull request should try its very best 14 | to address only a single concern. 15 | - If you plan to do further work after the PR has been submitted, please use the 16 | [Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/) 17 | feature. 18 | - Make sure all tests pass and add additional tests for the code you submit. 19 | [More info here](https://docs.forem.com/tests/). 20 | - Document your reasoning behind the changes. Explain why you wrote the code in 21 | the way you did. The code should explain what it does. 22 | - If there's an existing issue related to the pull request, reference to it by 23 | adding something like `References/Closes/Fixes/Resolves #305`, where 305 is 24 | the issue number. 25 | [More info here](https://github.com/blog/1506-closing-issues-via-pull-requests). 26 | - Please fill out the PR Template when making a PR. 27 | - Use **Reviewers** section to request for reviews. 28 | - All commits in a pull request will be squashed when merged, but when your PR 29 | is approved and passes our CI, it will eventually be live on production! 30 | 31 | If the pull request affects the public API in any way, a post on Forem from the 32 | Forem Team account should accompany it. This is the duty of the core team to 33 | carry out. 34 | -------------------------------------------------------------------------------- /docs/getting-started/syncing.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Keeping Your Fork in Sync 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Now that you have a fork of Forem's source code, there is work you will need to 14 | do to keep it updated. 15 | 16 | ## Setup your upstream 17 | 18 | Inside your Forem directory, add a remote to the official Forem repo: 19 | 20 | ```shell 21 | git remote add upstream https://github.com/forem/forem.git 22 | ``` 23 | 24 | ## Rebasing from upstream 25 | 26 | Do this prior to creating each branch for a PR: 27 | 28 | Make sure you are on the main branch: 29 | 30 | ```shell 31 | $ git status 32 | On branch main 33 | Your branch is up-to-date with 'origin/main'. 34 | ``` 35 | 36 | If you aren't on `main`, finish your work and checkout the `main` branch: 37 | 38 | ```shell 39 | git checkout main 40 | ``` 41 | 42 | Do a pull with rebase against `upstream`: 43 | 44 | ```shell 45 | git pull --rebase upstream main 46 | ``` 47 | 48 | This will pull down all of the changes to the official `main` branch, without 49 | making an additional commit in your local repo. 50 | 51 | (Optional) Force push your updated `main` branch to your GitHub fork 52 | 53 | ```shell 54 | git push origin main --force 55 | ``` 56 | 57 | This will overwrite the `main` branch of your fork. 58 | 59 | ## Keeping your branch up to date 60 | 61 | Sometimes, your forked branch may get out of date. To get it up to date it, 62 | carry out the following steps: 63 | 64 | Rebase from upstream once again: 65 | 66 | ```shell 67 | git checkout main 68 | git pull --rebase upstream main 69 | ``` 70 | 71 | Checkout your feature branch locally and merge main back into your branch: 72 | 73 | ```shell 74 | git checkout 75 | git merge main 76 | ``` 77 | 78 | Merge any conflicts in editor if necessary: 79 | 80 | ```shell 81 | git commit -m "Fix merge conflicts" 82 | ``` 83 | 84 | Push the changes back to your origin feature branch: 85 | 86 | ```shell 87 | git push origin 88 | ``` 89 | 90 | After you've fetched new commits from upstream, run `./bin/setup`, and it will 91 | install new gems, npm packages, update database, and restart Rails server. 92 | 93 | ## Additional resources 94 | 95 | - [Syncing a fork](https://help.github.com/articles/syncing-a-fork/) 96 | -------------------------------------------------------------------------------- /docs/getting-started/workflow.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Suggested Workflow 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | We use [Spring](https://github.com/rails/spring), and it is already included in 14 | the project. 15 | 16 | 1. Use the provided bin stubs to automatically start Spring, i.e. 17 | `bin/rails server`, `bin/rspec spec/models/`, `bin/rails db:migrate`. 18 | 1. If Spring isn't picking up on new changes, use `spring stop`. For example, 19 | Spring should always be restarted if there's a change in the environment 20 | keys. 21 | 1. Check Spring's status whenever with `spring status`. 22 | 23 | Caveat: `bin/rspec` is not equipped with Spring because it affects Simplecov's 24 | result. Instead, use `bin/spring rspec`. 25 | 26 | ## Synchronizing a fork with upstream / integrate latest changes 27 | 28 | When changes in the upstream repository happen, the fork does not get those 29 | automatically and this is by design. To integrate the changes to the upstream 30 | repo that were committed since you cloned your fork or synced the last time, use 31 | the following script: `./scripts/sync_fork.sh` This will fetch the changes and 32 | merge them into your current workspace. 33 | 34 | Use this: 35 | 36 | - to get commits from upstream main into your branch 37 | - to sync with latest changes from upstream main before continuing with a new 38 | feature on your current branch 39 | 40 | After you've fetched new commits from upstream, run `./bin/setup`, and it will 41 | install new gems, npm packages, update database, and restart Rails server. 42 | 43 | Start the app by running `./bin/startup`, if it's not already running. 44 | 45 | ## Start over / discard all your changes 46 | 47 | Sometimes it is necessary to start over from the beginning or reset the current 48 | workspace to the state of the upstream branch. Use the helper 49 | `./scripts/clean_fork.sh` to set your fork to the exact same state as the 50 | upstream main branch. 51 | 52 | Use this: 53 | 54 | - before working on a new feature 55 | - before creating a new branch to make sure to have all the latest changes in 56 | your fork also. 57 | 58 | After you've done that, run `./bin/setup`, and it will update gems, npm 59 | packages, update database, and restart Rails server. 60 | 61 | Start the app by running `./bin/startup`, if it's not already running. 62 | -------------------------------------------------------------------------------- /docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | slug: / 4 | --- 5 | 6 | # Introduction 👋 7 | 8 | :::important 9 | 10 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 11 | 12 | ::: 13 | 14 | ## Welcome to Forem's developer documentation 🌱 15 | 16 | On this site you'll find instructions to setup a [local instance of 17 | Forem](./getting-started/installation/mac), documentation on the [architecture of 18 | Forem](./technical-overview/architecture/), [how to contribute](./contributing-guide/docs), and many other useful 19 | documents. 20 | 21 | This documentation site is the product of a number of volunteer contributors 22 | working alongside the Forem Core Team, special thanks to all those who have 23 | contributed to the documentation. 24 | 25 | :::important 26 | 27 | To set up and self-host your own Forem we suggest using our [Forem Self-host Installation Documentation](https://github.com/forem/selfhost) 28 | 29 | ::: 30 | -------------------------------------------------------------------------------- /docs/licensing.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 12 3 | --- 4 | 5 | # Licensing 6 | 7 | This program is free software: you can redistribute it and/or modify it under 8 | the terms of the GNU Affero General Public License as published by the Free 9 | Software Foundation, either version 3 of the License, or (at your option) any 10 | later version. Please see the 11 | [LICENSE](https://github.com/forem/forem/blob/main/LICENSE.md) file in our 12 | repository for the full text. 13 | 14 | Like many open source projects, we require that contributors provide us with a 15 | Contributor License Agreement (CLA). By submitting code to the Forem project, 16 | you are granting us a right to use that code under the terms of the CLA. 17 | 18 | Our version of the CLA was adapted from the Microsoft Contributor License 19 | Agreement, which they generously made available to the public domain under 20 | Creative Commons CC0 1.0 Universal. 21 | 22 | ## Where can I learn more about the AGPL-3 license? 23 | 24 | Please refer to the official 25 | “[Why the Affero GPL](https://www.gnu.org/licenses/why-affero-gpl.html)” and the 26 | [FAQ page](https://www.gnu.org/licenses/gpl-faq.html) on 27 | [GNU.org](https://www.gnu.org) for full details regarding this license. 28 | 29 | ## Why does Forem have a CLA? 30 | 31 | The Contributor License Agreement (CLA) is a legal document in which you state 32 | that you are entitled to contribute your code to the Forem codebase, and are 33 | willing to have it used in distributions and derivative work. 34 | 35 | The CLA helps provide confidence to future users that they will be allowed to 36 | use the Forem codebase without fear that a contributor will try to withdraw 37 | permissions at a later date. 38 | 39 | ## Am I giving away the copyright of my contributions? 40 | 41 | No. You are granting Forem/DEV a license to use and distribute your contribution 42 | without further restriction, not assigning the copyright. 43 | 44 | ## What if I have other questions? 45 | 46 | Please check out our main repository or send an email to 47 | [yo@dev.to](mailto:yo@dev.to). 48 | -------------------------------------------------------------------------------- /docs/maintainers/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Maintainers Guide", 3 | "position": 14 4 | } -------------------------------------------------------------------------------- /docs/maintainers/deploying.md: -------------------------------------------------------------------------------- 1 | # Deploying Forem 2 | 3 | Anyone with the ability to merge PRs on GitHub can deploy the application. 4 | **Whenever a PR is merged the code is deployed. When deploying complex code, be 5 | sure that other team members are around to help if something goes wrong.** 6 | 7 | Generally, it's a good idea to keep the SRE team in the loop on high risk 8 | deploys. However, deployments are our collective responsibility, so it's 9 | important to monitor your deploys. You can see deployment status on 10 | Travis-ci.com and in the #deployment-pipeline channel on Slack. Be prepared to 11 | rollback or push a fix for any deployment! 12 | 13 | # Deployment and CI/CD Process 14 | 15 | :::important 16 | 17 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 18 | 19 | ::: 20 | 21 | ## Overview 22 | 23 | Forem relies on GitHub and Travis to deploy continuously to Heroku. If a Pull 24 | Request is merged it will automatically be deployed to production once the build 25 | steps complete successfully. The process currently takes about 20 minutes to 26 | complete and will need a few additional minutes before the change goes live. 27 | 28 | ## Travis Stages 29 | 30 | The following stages can be explored in our 31 | [.travis.yml](https://github.com/forem/forem/blob/main/.travis.yml) and 32 | [Procfile](https://github.com/forem/forem/blob/main/Procfile). Our Travis CI 33 | process consists of 2 stages. 34 | 35 | 1. Running our test suite in 3 parallel jobs. 36 | 2. Deploying the application. 37 | 38 | ### Stage 1: Running Tests 39 | 40 | In stage 1, we use [KnapsackPro](https://knapsackpro.com/) to divide our Rspec 41 | tests evenly between 3 different jobs (virtual machines). This ensures that each 42 | job takes relatively the same amount of time to run. After running our Rspec 43 | tests, we then run a series of other checks. These additional checks are split 44 | up between the different jobs. Here is a list of those additional checks that 45 | are run. 46 | 47 | - Job 0 is where we run JavaScript tests, Preact tests, and coverage checks. 48 | - Job 1 is where Travis builds Storybook to ensure its integrity, and where we 49 | check for any known vulnerabilities using `bundle-audit`. 50 | - Job 2 is where Travis fires up a Rails console to ensure the application loads 51 | properly. 52 | 53 | If all of the jobs pass then we move on to Stage 2 of the Travis CI process. 54 | 55 | ### Stage 2: Deploying 56 | 57 | If the build was kicked off from a pull request being created or updated this 58 | stage will do nothing. If the branch has been merged into main, then this stage 59 | will kick off a deploy. The deploy will run in its own job deploying our 60 | application to Heroku. 61 | 62 | Prior to deploying the code, Heroku will run database migrations and do some 63 | final checks (more information on that below) to make sure everything is working 64 | as expected. If these all succeed, then the deploy completes and our team is 65 | notified. 66 | 67 | ## Deploying to Heroku 68 | 69 | We use Heroku's 70 | [Release Phase](https://devcenter.heroku.com/articles/release-phase) feature. 71 | Upon deploy, the app installs dependencies, bundles assets, and gets the app 72 | ready for launch. However, before it launches and releases the app Heroku runs a 73 | release script on a one-off dyno. If that release script/step succeeds the new 74 | app is released on all of the dynos. If that release script/step fails then the 75 | deploy is halted and we are notified. 76 | 77 | The name of the script we use is `release-tasks.sh` and its in our root 78 | directory. During this release step we do a few checks. 79 | 80 | 1. We first check the DEPLOY_STATUS environment variable. In the event that we 81 | want to prevent deploys, for example after a rollback, we will set 82 | DEPLOY_STATUS to "blocked". This will cause the release script to exit with a 83 | code of 1 which will halt the deploy. This ensures that we don't accidentally 84 | push out code while we are waiting for a fix or running other tasks. 85 | 2. We run any outstanding migrations. This ensures that a migration finishes 86 | successfully before the code that uses it goes live. 87 | 3. We run any data update scripts that need to be run. A data update script is 88 | one that allows us to update data in the background separate from a 89 | migration. 90 | 4. Following updating all of our datastores we use the Rails runner to output a 91 | simple string. Executing a Rails runner command ensures that we can boot up 92 | the entire app successfully before it is deployed. We deploy asynchronously, 93 | so the website is running the new code a few minutes after deploy. A new 94 | instance of Heroku Rails console will immediately run a new code. 95 | -------------------------------------------------------------------------------- /docs/maintainers/pull_requests.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Pull Requests 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## General 14 | 15 | - We generally follow a branch naming convention of 16 | `/-`, e.g. `sloan/make-things-awesome-5555`. 17 | Issue numbers can be omitted when unavailable, e.g. for things like quick 18 | fixes, spikes, experiments, etc. 19 | - Push early and often, you don’t need to wait until you’re finished to get 20 | feedback. Please use GitHub draft PRs when you have a work-in-progress (WIP) 21 | PR, or when you have a PR that is contingent on something else to be merged. 22 | We prefer draft PRs rather than adding "DO NOT MERGE" to a PR's title. Any PR 23 | that is not a draft should be ready to be merged by anyone once it has been 24 | reviewed. 25 | - Please follow our pull request template. Provide context for reviewers, when 26 | in doubt err on the side of too much information. 27 | - Core team members are not required to review draft PRs. To explicitly request 28 | feedback, please assign or mention people. 29 | 30 | ## PR Reviews 31 | 32 | PR review is the Core Team's opportunity to weigh-in on changes before they go 33 | into production. 34 | 35 | Keep in mind that our team is distributed across the world. It's a good idea to 36 | leave far-reaching PRs open for a day, to give everyone a change to share their 37 | thoughts. 38 | 39 | - We require 2 approvals from core team members for each PR: 40 | - One from the same team because they have context and are working towards the 41 | same goal as you. 42 | - One from outside your team for the following reasons: 43 | - A different perspective from someone who has less context 44 | - To leverage our different strengths and garner additional insights 45 | spreading knowledge and code exposure throughout the team 46 | - To avoid overloading individuals 47 | - Tag people that you’d like to review your PR using GitHub’s ‘Reviewers’ 48 | function. 49 | - Be kind! The goal here is to work together on shipping good code, not to judge 50 | people. 51 | - For serious issues, use Github’s “Request changes” feature. This should be 52 | reserved for serious problems, e.g. 53 | - Doesn’t do what the issue said 54 | - Foreseeable performance problems 55 | - Provable security issues 56 | - Breaks existing functionality 57 | - Everything else can be posted as a comment, but it’s up to the original PR 58 | author whether or not they want to incorporate the changes. 59 | - Please keep style discussions to an absolute minimum, that time is better 60 | spent making a PR for the configuration of our various lint tools. 61 | - If you make changes to your PR, please re-request feedback from previous 62 | reviewers. 63 | 64 | # Merging Pull Requests 65 | 66 | At the time of writing, we tend to prefer squashing PRs into a single commit and 67 | merging them. This is easily (and safely) achieved using the GitHub UI. 68 | 69 | All required CI checks should be green. 70 | 71 | Core team members are responsible for merging their own pull requests and pull 72 | requests issued by those outside the core team. 73 | 74 | Once a PR is merged, it might need to be deployed. Deployment is a team 75 | responsibility, and everyone on the core team should be comfortable deploying 76 | code. For more information, read the 77 | [deployment guide](https://docs.forem.com/maintainers/deployment). 78 | -------------------------------------------------------------------------------- /docs/self-hosting.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Self Hosting Forem 3 | --- 4 | 5 | ## Hosting your own Forem instance 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Visit [the official Forem Self-Host playbook](https://github.com/forem/selfhost) in order to host on DigitalOcean, AWS, Google Cloud, or bare metal. 14 | 15 | If you intend to stand up a Forem, we _strongly_ encourage you to join [forem.dev](https://forem.dev) to take part and keep up with changes. 🌱 16 | -------------------------------------------------------------------------------- /docs/technical-overview/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Technical Overview", 3 | "position": 2 4 | } -------------------------------------------------------------------------------- /docs/technical-overview/ab-testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # A/B testing 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | We use the [Field Test](https://github.com/ankane/field_test) gem for conducting 14 | simple A/B tests. 15 | 16 | If you want to propose an A/B test of a feature, you may make a pull request 17 | which defines the hypotheses and what admins should look for to declare a 18 | winner. As A/B tests are going to have results that may differ from Forem to 19 | Forem, the process is relatively immature. In the future we may have more 20 | studies that can return anonymous ecosystem-wide results. 21 | 22 | A/B tests are inherently the most useful in _large_ Forems, where qualitative 23 | feedback may be more useful on small Forems. As such, [DEV](https://dev.to) is 24 | our largest Forem and therefore can provide us with the most feedback for our 25 | existing A/B tests. However, we must keep in mind that DEV results may not apply 26 | well to future large Forems. We should seek to re-run useful experiments within 27 | the ecosystem after time has passed. 28 | 29 | ## Creating a new A/B test 30 | 31 | Follow the guidelines of the field test gem and add the test info to 32 | [config/field_test.yml](https://github.com/forem/forem/blob/main/config/field_test.yml). 33 | 34 | Then where you want to trigger the variant, you'll add some code like this: 35 | 36 | ```ruby 37 | test_variant = field_test(:follow_implicit_points, participant: user) 38 | case test_variant 39 | when "no_implicit_score" 40 | 0 41 | when "half_weight_after_log" 42 | Math.log(occurrences + bonus + 1) * 0.5 43 | when "double_weight_after_log" 44 | Math.log(occurrences + bonus + 1) * 2.0 45 | when "double_bonus_before_log" 46 | Math.log(occurrences + (bonus * 2) + 1) 47 | when "without_weighting_bonus" 48 | Math.log(occurrences + 1) 49 | else # base - Our current "default" implementation 50 | Math.log(occurrences + bonus + 1) # + 1 in all cases is to avoid log(0) => -infinity 51 | end 52 | ``` 53 | 54 | Which would find or create the test variant for that user in particular. If this 55 | code is not called in the controller or view, you'll need to first include the 56 | gem helpers at the top of the file... 57 | 58 | ``` 59 | include FieldTest::Helpers 60 | ``` 61 | 62 | To record a successful field test outcome, you should call something like this 63 | 64 | ```ruby 65 | Users::RecordFieldTestEventWorker 66 | .perform_async(user_id, :follow_implicit_points, "user_creates_reaction") 67 | ``` 68 | 69 | And modify that class as needed to determine whether to record the successful 70 | trial. 71 | -------------------------------------------------------------------------------- /docs/technical-overview/caching.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 8 3 | --- 4 | 5 | # Caching 6 | 7 | 8 | _This has been adapted from [The Three Caches of Forem // Take on Rules](https://takeonrules.com/2022/03/29/the-three-caches-of-forem/)_ 9 | 10 | 11 | In the Forem code base, we make extensive use of various caching strategies. And as with any cache, we always run the risk of not invalidating the right caches. 12 | 13 | You can read more about our response caching strategies here: 14 | https://dev.to/devteam/caching-at-dev-11el 15 | 16 | We also dive more into Rails Caching which touches on (Fragment Caching)and Database Caching below. 17 | 18 | At a high level, we leverage caches to improve performance. Let’s start of by introducing by detailing what a Response Document and Fragment is. 19 | 20 | The response document is a single file sent from the server in response to a request. 21 | 22 | Remember that what you see rendered in your browser is almost always from the combination of many _response documents_ sent from the server: HTML documents, Javascript files, CSS files, and XHR that modify the DOM. 23 | 24 | To build a single _response document_, the server often assembles multiple fragments to form that singular document. In Ruby on Rails this is done with like layouts, views, and partial views. 25 | 26 | ## Rails Caching 27 | 28 | We use the Rails cache to store all kinds of things, mostly fragments that we use to build the response document. 29 | 30 | Each entry we put into the Rails cache has a unique key. When we use the cache, we check if the key exists. If so we use what’s in the cache for that key. Otherwise, we run the cached logic and put it in the cache with that key. 31 | 32 | What does that key look like? It depends. But in a general sense it often includes a timestamp. Let’s create a quick and arbitrary example. 33 | 34 | Let’s say it is very expensive to generate the HTML for an article’s card. We choose to cache the article’s card’s HTML. The key for that might like like the following: `article--` 35 | 36 | Then when we check the cache, we use the article’s ID and when it was last updated. In this way, older updates to the article might still be in the cache, but we’re not going to fetch those. 37 | 38 | There are more complicated strategies we use. As part of the site’s layout we cache information; the community name, tracking analytics, last deploy date, last commit id, and more. 39 | 40 | When an admin makes a site wide change, that prompts to use a new key; so long as we remember to update the attributes in the key. This [pull request](https://github.com/forem/forem/pull/17040) resolved one of the issues (in a not yet available in production) when we didn’t update the attribute keys. 41 | 42 | You can learn more about [Caching with Rails](https://guides.rubyonrails.org/caching_with_rails.html) over at the Rails Guides. 43 | 44 | ## Database Caching 45 | 46 | The last is database caching. We cache an article’s tag list, user, and organization information (if applicable). These are cached as fields on the articles table. The purpose of these cached attributes is performance. Where possible, we prefer to minimize joins for queries that we frequently perform (e.g. get me a list of articles being a very common query for a Forem). 47 | 48 | On we reported an issue where [changing an organization’s image should invalidate the associated articles cached organization (Issue #17041)](https://github.com/forem/forem/issues/17041). The solution is whenever we update an organization we should revisit each article associated with that organization. 49 | 50 | This [pull request](https://github.com/forem/forem/pull/17052) looked to fix the reported error and provides points in the code where we're caching at the database layer. 51 | 52 | ## Way-finding in the Era of Caching 53 | 54 | To properly troubleshoot a caching issue, a recommendation is to request a screenshot and an arrow pointing to the specific thing that isn’t updating. This will help track down in the code which caching strategy might be in play. -------------------------------------------------------------------------------- /docs/technical-overview/compatibility.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Compatibility 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | To go along with effective administration and putting user security and 14 | well-being first, for a Forem to be most useful, it maximizes its technical 15 | compatibility. 16 | 17 | Compatibility means: 18 | 19 | - Staying updated. We need to maintain overall compatibility by keeping the 20 | ecosystem close to the latest version as proactively as we can. 21 | - Maintaining all programmatic interfaces, whether they be the 22 | [API](https://api.forem.com) or Forem meta tags which can be consumed for 23 | Forem validation. 24 | - Maintaining human interfaces which allow users to find where they need to go 25 | and conduct all necessary customizations. 26 | - Maintaining great performance such that the web-based Forem ecosystem can 27 | provide a great experience in all network conditions and in comparison to more 28 | closed ecosystems. 29 | 30 | ## Meta tags 31 | 32 | The canonical meta tags which are expected to be present as a component of 33 | validation are as follows: 34 | 35 | ```html 36 | 37 | 38 | 39 | ``` 40 | 41 | This list may evolve over time. If you want to propose a new meta tag which 42 | could improve the efficacy of an ecosystem app, please feel free to open an 43 | issue or a discussion on [forem.dev](https://forem.dev). 44 | -------------------------------------------------------------------------------- /docs/technical-overview/feature-flags.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Feature flags 6 | 7 | We sometimes employ feature flags to develop features that aren't fully formed 8 | at the moment of deployment. They can also be employed to roll out features in 9 | production incrementally. 10 | 11 | Feature flags are meant to be **temporary** and part of a rollout plan resulting in 12 | their removal. 13 | 14 | Since we do not expose feature flags to Forem Creators, we have built in the concept of an extension which ties in closely with Feature Flags. You can view Extensions at `/admin/advanced/extensions`. 15 | ## Creating a new feature flag 16 | 17 | To create a new feature flag, we use 18 | [data update scripts](../backend/data-update-scripts) so that they can be made 19 | available over the entire Forem fleet, for example: 20 | 21 | ```shell 22 | bundle exec rails g data_update AddAwesomeAlgorithmFeatureFlag 23 | ``` 24 | 25 | This will generate a script and its spec. Once done, you can add the new feature 26 | flag like this: 27 | 28 | ```ruby 29 | module DataUpdateScripts 30 | class AddAwesomeAlgorithmFeatureFlag 31 | def run 32 | FeatureFlag.add(:awesome_algorithm) 33 | end 34 | end 35 | end 36 | ``` 37 | 38 | and then start using it right away. 39 | 40 | ## Guarding a feature behind a flag 41 | 42 | Once the feature flag is added, you can start using it to hide the feature 43 | behind a boolean flag: 44 | 45 | ```ruby 46 | if FeatureFlag.enabled?(:awesome_algorithm) 47 | # call the new code 48 | else 49 | # call the previous code 50 | end 51 | ``` 52 | 53 | or, for example in a view: 54 | 55 | ```erb 56 | <% if FeatureFlag.enabled?(:blue_button) %> 57 | <%= render "blue_button" %> 58 | <% else %> 59 | <%= render "good_old_button" %> 60 | <% end %> 61 | ``` 62 | 63 | ## Enabling/disabling a feature flag globally 64 | 65 | As mentioned, feature flags can be used to test a work in progress feature or to 66 | test a new approach directly in production. 67 | 68 | To enable such a feature: 69 | 70 | - Log in to the Forem 71 | - Go to `/admin/feature_flags` (it requires the 72 | [`tech_admin` role](../backend/roles)) 73 | - Click on the flag you want to enable and press "Fully Enable". 74 | 75 | ![A screenshot of the Admin Feature Flag Panel](/img/docs/admin_feature_flags.png) 76 | 77 | ## Removing a feature flag 78 | 79 | Once the feature has been validated and finalized or discarded, please remember 80 | to remove its flag with a final data update script, for example: 81 | 82 | ```shell 83 | bundle exec rails g data_update RemoveAwesomeAlgorithmFeatureFlag 84 | ``` 85 | 86 | ```ruby 87 | module DataUpdateScripts 88 | class AwesomeAlgorithmFeatureFlag 89 | def run 90 | FeatureFlag.remove(:awesome_algorithm) 91 | end 92 | end 93 | end 94 | ``` 95 | 96 | This will ensure that the feature will be removed from all Forems. 97 | -------------------------------------------------------------------------------- /docs/technical-overview/feed.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Feed 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## "The feed" 14 | 15 | The Forem feed is perhaps _the_ core functionality of the service. It's an adaptation of a concept popularized by other social platforms in the past two decades, and it is something we need to develop with care in a way that empowers individual communities and users. 16 | 17 | The core nature of "the feed" is that it needs to evolve and be flexible. We will learn new ideas over time. We need to take into account metrics, but also question the relevance and "goodness" of certain metrics. We want folks finding fulfilling and enriching content, not necessarily addictive content. 18 | 19 | However, we are in the fairly naive early days of the feed, so primarily it is a matter of flexibility and experimentation. 20 | 21 | There are three conceptual "feeds": 22 | 23 | - Relevant :: articles that are most relevant to the user's stated preferences 24 | - Latest :: the most recent list of articles 25 | - Top :: a mix of high quality and much discussed articles within a given time frame 26 | 27 | ### Feed style 28 | 29 | Each Forem can have a feed style set by the admin of that community (originally implemented [in a PR from mid-2020](https://github.com/forem/forem/pull/8721)). 30 | 31 | Currently, we have three styles: 32 | 33 | - `basic` :: hide the cover image from the feed view 34 | - `compact` :: a more minimal feed style 35 | - `rich` :: always displays the cover image in the feed 36 | 37 | Over time, we'll continue to improve and expand our feed style options. 38 | 39 | See the [`Settings::UserExperience` code](https://github.com/forem/forem/blob/main/app/models/settings/user_experience.rb) for the current state styles. 40 | 41 | ### Feed strategy 42 | 43 | Each Forem can have a feed strategy set by the admin of that community (originally implemented [in a late-2020 PR](https://github.com/forem/forem/pull/10245)). Currently, we have two strategies: `basic` and `large_forem_experimental`. The "experimental" component dictates that there is some split testing, but generally these are just cues for an underlying algorithm which can change liberally. 44 | 45 | The feed endpoint is driven by the `feeds_controller` and the content is found in objects such as `Articles::Feeds::Basic` and `Articles::Feeds::VariantQuery`. We should lean toward adaptability and versatility in the long run here, even if we are just at the beginning of this transparent journey. 46 | 47 | Learn more about the experimental feed and our split testing in the [code's feeds README](https://github.com/forem/forem/blob/main/app/models/articles/feeds/README.md). 48 | -------------------------------------------------------------------------------- /docs/technical-overview/i18n.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Internationalization (i18n) 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Forem's internationalization is currently in its early stages. You are *encouraged* to follow patterns laid out here, but it is not yet *required* for every pull request until further development and stabilization of the feature. 14 | 15 | This development is a matter of translation as well as decoupling of copy from code. As this functionality is new, best practices in this area are still a work in progress. 16 | 17 | ## i18n features and direction 18 | 19 | Admins can choose a default locale among those currently supported. This feature is not complete and marked as experimental. This functionality will translate the display of the site (buttons, labels, etc.) and *not* user-generated content. It does not use the browser's locale as a hint. It only allows admins to set a default locale. 20 | 21 | The site is also translatable via the dedicated path of `/locale/:locale`. So if a user types in `/locale/fr` they will get the french translation. This is also experimental and not technically "in use", but it does lay the framework for future development where we detect the language of the user-generated content on the page and serve the locale URL _as the canonical page_ (not the one visible to the end user, they will still see the locale determined by the admin). 22 | 23 | Future development should include members overriding the default locale as a setting so that any Forem user can use any Forem in their native language. This could involve additional caching complexity and is not yet in the works. 24 | 25 | We are currently not localizing the admin backend _at all_, as that area has more nuanced communication and is generally less mature than the member-facing site. 26 | 27 | ## Contributing 28 | 29 | The most helpful contributions are anything that gets the project a step closer to full localization. A PR that tries to do it all at once will not be as helpful. Any opportunity to _remove_ copy entirely that is not necessary is also a step in the right direction and can be considered at pull-request time as an option, though that has a higher chance of being rejected due to the change to product functionality vs an objective improvement to localization. 30 | 31 | We use [standard Rails localization practices](https://guides.rubyonrails.org/i18n.html). We have files related to each locale such as `config/locales/fr.yml`. 32 | 33 | The yml file will look like this... 34 | 35 | ```yml 36 | --- 37 | fr: 38 | core: 39 | add_comment: Ajouter un commentaire 40 | comment: Commentaire 41 | copy_link: Copier le lien 42 | create_account: Créer un profil 43 | ``` 44 | 45 | And in HTML, these can be used as helpers, such as... 46 | 47 | ```html 48 |
<%= t("core.add_comment") %>
49 | ``` 50 | 51 | `t` in this view helper context is short for `I18n.translate`. Check out the linked Rails guide for full information about how to use all standard translation and localization functionality. 52 | 53 | ### i18n.js 54 | 55 | We use the [i18n.js](https://github.com/fnando/i18n-js) gem to pass values to JavaScript. Our frontend code is currently divided into old asset pipeline code in `app/assets/javascripts` and modern Rails JS practices at `app/javascript`. In the asset pipeline you should directly use `I18n.t` and in `app/javascript`, there is a wrapper at `app/javascript/utilities/locale.js` you can include as you go. This is all subject to change in the name of code quality improvements. 56 | 57 | This should generally be abstracted away for you if you follow above patterns, but for your information, we store translations in the view like this: 58 | 59 | ```html 60 |
61 | ``` 62 | 63 | And originally access them in JavaScript like this: 64 | 65 | ```js 66 | const translationsDiv = document.getElementById('i18n-translations') 67 | ``` 68 | 69 | This approach helps ensure we don't need to ship _all_ translations _in_ JS files, but is somewhat naive and could be improved over time. 70 | 71 | ### i18n-tasks 72 | 73 | We use the [i18n-tasks](https://github.com/glebm/i18n-tasks) gem to manage updating the localization files as changes are made. It is not yet fully integrated into the test suite, but will be as a matter of ensuring consistency of internationalization. This gem provides several helpers for automatically translating missing values, some of which can plug into cloud translation services such as Google Translate, which will require you to add a key to the appropriate service. This is optional local functionality and does not constitute a hard dependency on development. -------------------------------------------------------------------------------- /docs/technical-overview/stack.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Stack 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## 🔑 Key App tech/services 14 | 15 | For the Forem tech stack we use: 16 | 17 | - [_Puma_](https://github.com/puma/puma) as the web server 18 | - [_PostgreSQL_](https://www.postgresql.org/) as the primary database and for Full Text Search 19 | - [_Redis_](https://redis.io/) to store cached data 20 | - [_Fastly_](https://www.fastly.com/) for [edge caching](https://dev.to/ben/making-devto-insanely-fast) 21 | - [_Cloudinary_](https://cloudinary.com/) and/or [_Imgproxy_](https://github.com/imgproxy/imgproxy) for image manipulation/serving 22 | - [_Honeybadger_](https://www.honeybadger.io/) for error monitoring 23 | - [_Sidekiq_](https://github.com/mperham/sidekiq) and [_Active Job_](https://guides.rubyonrails.org/active_job_basics.html) for background workers 24 | - [Ransack](https://github.com/activerecord-hackery/ransack) for internal search 25 | - [_Redcarpet_](https://github.com/vmg/redcarpet) and [_Rouge_](https://github.com/jneen/rouge) to parse Markdown 26 | - [_Carrierwave_](https://github.com/carrierwaveuploader/carrierwave), [_Fog_](https://github.com/fog/fog-aws) and [_AWS S3_](https://aws.amazon.com/s3/) for image upload/storage 27 | - [_InstantClick_](http://instantclick.io/) (a modified version) instead of _Turbolinks_ to accelerate navigation 28 | - [_ImageMagick_](https://imagemagick.org/) to manipulate images on upload 29 | - [_Heroku_](https://www.heroku.com) for hosting 30 | - [_Sendgrid_](https://sendgrid.com/) for transactional mailing 31 | - [_Mailchimp_](https://mailchimp.com/) for marketing/outreach emails 32 | - [_CounterCulture_](https://github.com/magnusvk/counter_culture) to keep track of association counts (counter caches) 33 | - [_Rolify_](https://github.com/RolifyCommunity/rolify) for role management 34 | - [_Pundit_](https://github.com/varvet/pundit) for authorization to proxy traffic 35 | - [Preact](https://preactjs.com/) for some of the frontend. See [the Frontend Guide](../frontend/javascript-approaches/preact) for more info 36 | - [_Docusaurus_](https://docusaurus.io/) for beautiful and SEO-friendly documentation 37 | - [Git](https://git-scm.com/) for version control 38 | - [GitHub](https://github.com/) for hosting the source code and issue tracking 39 | -------------------------------------------------------------------------------- /docs/tests/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Testing/QA Guide", 3 | "position": 9 4 | } -------------------------------------------------------------------------------- /docs/tests/code-coverage.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Code coverage 6 | 7 | ## Codecov 8 | 9 | We use [Codecov](https://docs.codecov.com/docs) to track code coverage. Codecov leaves a comment PR's that indicate the a percentage change in code coverage compared to main. We encourage you to keep the code coverage percentage at 80% and above. 10 | 11 | ## Rails 12 | 13 | Rspec will generate code coverage at the end of the tests. 14 | 15 | To get the code coverage of the entire Rails codebase, you must run the full 16 | Ruby test suite. You can run the full test suite with the `rspec` command: 17 | 18 | ```shell 19 | bundle exec rspec 20 | ``` 21 | 22 | or 23 | 24 | ```shell 25 | bin/rspec 26 | ``` 27 | 28 | To get the code coverage of a single file, you can run: 29 | 30 | ```shell 31 | bundle exec rspec spec/models/user_spec.rb 32 | ``` 33 | 34 | or 35 | 36 | ```shell 37 | bin/rspec spec/models/user_spec.rb 38 | ``` 39 | 40 | To get the code coverage of a particular spec in a single file, append the 41 | line-number for that spec: 42 | 43 | ```shell 44 | bundle exec rspec spec/models/user_spec.rb:24 45 | ``` 46 | 47 | or 48 | 49 | ```shell 50 | bin/rspec spec/models/user_spec.rb:24 51 | ``` 52 | 53 | Once your tests have completed, the `coverage/index.html` will be regenerated 54 | with some stats concerning the overall health of our test suite including a code 55 | coverage metric. 56 | 57 | A few things to note: 58 | 59 | - "Coverage" indicates whether or not the test suite runs through the code in 60 | question. It does not equate to actually testing for functionality, and 61 | shouldn’t be thought of that way. 62 | - Running Rspec in general will overwrite the existing `coverage/index.html` so, 63 | if you want to reference the results of a particular run, save a copy of the 64 | file before re-running the test suite. 65 | 66 | To turn off coverage report generation please set environment variable 67 | `COVERAGE` value to `false`. 68 | 69 | ## Preact 70 | 71 | Preact tests will generate code coverage at the end of the tests. 72 | 73 | To get the code coverage of the Preact codebase, you must run the full JS test 74 | suite. You can run the full test suite with the npm task `test`: 75 | 76 | ```shell 77 | npm run test 78 | ``` 79 | 80 | or 81 | 82 | ```shell 83 | yarn test 84 | ``` 85 | 86 | Once the tests have completed, the test coverage metric will be visible in the 87 | terminal window. Please note that jest will fail if test coverage thresholds are 88 | not met. -------------------------------------------------------------------------------- /docs/tests/other-focus-areas/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Other Focus Areas", 3 | "position": 3 4 | } -------------------------------------------------------------------------------- /docs/tests/other-focus-areas/accessibility-tests.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Accessibility Tests 6 | 7 | Accessibility testing is a form of automated and manual testing that helps us 8 | identify some of the potential accessibility concerns on Forem. See also, the 9 | [Forem Frontend Accessibility docs](https://docs.forem.com/frontend/accessibility/). 10 | 11 | Many accessibility (a11y) issues are not obvious to everyone who contributes to 12 | the Forem project, therefore leaning on tools to help us identify these issues 13 | is a good practice. 14 | 15 | It's a good idea to use browser plugins while you're developing to keep an eye 16 | out for these issues, as well as including automated tests to catch regressions 17 | in future changes. 18 | 19 | The [axe devtools](https://www.deque.com/axe) extension is a great place to get 20 | started. It works in both Firefox and Chrome. Use an extension like this one to 21 | find potential a11y issues in your workflow. 22 | 23 | ## Automated testing in Preact 24 | 25 | An overarching a11y testing strategy isn't currently in place for the Forem 26 | application, but there are some automated tools you can take advantage of in 27 | this project. 28 | 29 | It is important to note that automated accessibility testing is only effective 30 | at finding ~30% of accessibility issues. It's important to do manual a11y 31 | testing as well. 32 | 33 | When you're writing Preact components, you can include some basic a11y testing 34 | in your unit tests with [jest-axe](https://github.com/nickcolley/jest-axe). 35 | 36 | If you're still curious 37 | [there are some great talks on accessibility for developers](https://www.youtube.com/watch?v=8E9AEZjglqI). 38 | 39 | ## PR Review Checklist 40 | 41 | Pull Requests should include accessibility testing to prevent barriers from 42 | making it into the codebase. Sometimes issues found may be out of scope for a 43 | particular PR – those can be opened as separate issues to be followed up on. 44 | 45 | Test the functionality in Forem-supported browsers for accessibility impact: 46 | Chrome and Firefox were popular for Windows screen reader users in 2019 47 | [according to WebAIM](https://webaim.org/projects/screenreadersurvey8/#browsers). 48 | Safari is the most common choice for Voiceover users on the Mac, even if Chrome 49 | is widely used for development. Mobile browsers are worth checking, too. 50 | 51 | 1. Keyboard: navigate the page without using a mouse or trackpad. 52 | - Can you reach and operate the interactive controls like menus, buttons, and 53 | other widgets? 54 | - Can you see your focus point on the screen, and does the focus style have 55 | adequate contrast? 56 | - Does your focus point get lost or hidden behind any layers with items 57 | needing to be disabled/hidden? 58 | 1. Run a browser extension. 59 | - Use [axe](https://deque.com/axe), 60 | [Accessibility Insights](https://accessibilityinsights.io), Chrome's 61 | Lighthouse Audit, or 62 | [WAVE](https://chrome.google.com/webstore/detail/wave-evaluation-tool/jbbplnpkjmmeebjpijfedlgcdilocofh) 63 | to run accessibility tests in the DevTools. 64 | - Prioritize relevant, higher-impact violations and issues first. 65 | 1. Test color contrast. 66 | - Pay extra attention to color contrast findings in your browser extension 67 | tests, since it's the most common accessibility issue on the internet. 68 | - The 69 | [Chrome color picker with contrast ratio line](https://developers.google.com/web/tools/chrome-devtools/accessibility/reference#contrast) 70 | and 71 | [WebAIM contrast checker](https://webaim.org/resources/contrastchecker/) 72 | are helpful tools for tweaking colors to meet 73 | [WCAG requirements](https://webaim.org/articles/contrast/). 74 | 75 | ### Additional accessibility tests for PR reviews 76 | 77 | 1. Screen reader testing 78 | - Get feedback from people who use screen readers regularly if at all 79 | possible. 80 | - Understand that if you're a new screen reader user, there might be a 81 | learning curve that can impact your results. 82 | - Navigate through a UI change using a screen reader such as Mac 83 | [Voiceover with Safari](https://webaim.org/articles/voiceover/), and 84 | [NVDA](https://webaim.org/articles/nvda/) with Firefox or Chrome on 85 | Windows. 86 | 1. Zoom and magnification 87 | - In the web browser, try zooming in from 200% to 500%. 88 | - Would the layout or change of functionality be impacted by someone needing 89 | to magnify their screen? Are there alternative styling or other solutions 90 | that would make zooming any easier? 91 | -------------------------------------------------------------------------------- /docs/tests/other-focus-areas/manual-tests.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Manual Tests 6 | 7 | We try to automate as much as possible, but particularly for frontend changes it 8 | is usually necessary to verify work with a manual test. When manually testing a 9 | feature, it's useful to check: 10 | 11 | - Does the UI look consistent across different desktop browsers? 12 | - Is the UI optimised for a mobile layout? 13 | - Does the feature behave consistently across desktop and mobile browsers? 14 | - Is the feature accessible? (See the 15 | [Accessibility testing docs](accessibility-tests.md)) 16 | 17 | ## Tips for testing on mobile 18 | 19 | For features involving touch interactions it becomes more important to test on 20 | actual mobile devices, rather than using the browser dev-tools device simulator. 21 | There can be some platform-specific differences in how these touch events are 22 | handled and it's useful to make sure a feature is checked on both android and 23 | iOS. 24 | 25 | One way to run your development code on your own mobile device is to use 26 | [ngrok](https://ngrok.com/). ngrok is a free tool that allows you to access your 27 | `localhost:3000` via a standard URL. To use it: 28 | 29 | - Follow [ngrok's instructions](https://ngrok.com/) to download/install the tool 30 | (there is a free tier) 31 | - Run `ngrok http 3000` in your terminal 32 | - Copy the URL given in response (e.g. `xxxxxxxxx.ngrok.io`) and add it to your 33 | `.env` file's `APP_DOMAIN` value (replacing `localhost:3000`) 34 | - Start the app via `bin/startup` as usual 35 | - Visit the ngrok URL on your mobile (to save copying the text, you could use a 36 | [QR code generator](https://www.qr-code-generator.com/free-generator/) on your 37 | desktop browser so you only need to point your camera at the screen) 38 | -------------------------------------------------------------------------------- /docs/tests/other-focus-areas/regression-tests.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Regression Testing 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | Regression testing is a technique for detecting breaking changes over time in a 14 | codebase. 15 | 16 | Proper Regression testing is one of the most powerful tools in a programmer's 17 | arsenal for avoiding the bugs that crop up as a project grows in size. 18 | 19 | Generally speaking, Regression testing is accomplished by a combination of 20 | [Feature tests](../types/feature-tests.md) and [Integration tests](../types/integration-tests.md) 21 | performed on areas where bugs have been found in the past. As it happens, 22 | software bugs tend to exist in groups; writing tests around buggy code will 23 | almost always uncover new bugs as code evolves. 24 | 25 | The practice of Regression testing usually takes place after code has been 26 | written, and is used to detect if recent changes have introduced new bugs by 27 | breaking previously functional software. 28 | 29 | There is no perfect prescribed method for Regression testing, and it has evolved 30 | with the introduction of [Continuous Integration][ci] practices like the ones 31 | that Forem uses. However, CI doesn't replace the need for Regression testing. 32 | Regression tests should still be added to the codebase when bugs are discovered. 33 | 34 | If you submit a bug patch to the Forem application, you might be asked to write 35 | a Regression test around your patch to help warn future Forem contributors if 36 | that bug ever makes another appearance. This practice has the added benefit of 37 | helping to ensure your patch fixes the bug. 38 | -------------------------------------------------------------------------------- /docs/tests/philosophy.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Philosophy 6 | 7 | ## Tests 8 | 9 | When adding any new features to the application, we always write tests alongside our code. This may be in the form of testing: 10 | 11 | - the user interface interactions from the end users perspective through Cypress [end to end tests](types/e2e-tests.md). 12 | - the interaction from a request perspective through [integration tests](types/integration-tests.md). 13 | - a single function/method in isolation with its possible inputs and outputs through [unit and functional tests](types/unit-functional-tests.md). 14 | - JavaScript code whether it be logic in a pack file or a Preact component through [frontend tests](types/frontend-tests.md). 15 | 16 | We also encourage [manual](other-focus-areas/manual-tests.md), [accessibility](other-focus-areas/accessibility-tests.md) and [regression](other-focus-areas/regression-tests.md) when the need arises. 17 | 18 | We track our code coverage in each new PR using Codecov. 19 | 20 | ## QA 21 | 22 | Whilst we do not have a dedicated QA environment, if it is essential that your branch gets tested on production before we can go live, we encourage you to either put it behind a feature flag or we can manually deploy the branch to a Forem for testing. 23 | -------------------------------------------------------------------------------- /docs/tests/types/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Types of tests", 3 | "position": 2 4 | } -------------------------------------------------------------------------------- /docs/tests/types/feature-tests.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Feature Tests (Legacy) 6 | 7 | :::important 8 | 9 | We no longer actively add feature tests to `/spec/system` since we have added Cypress as a tool for our end-to-end testing. Hence, we are in the process of replicating the current `/spec/system` tests in Cypress and in the future we will only use Rails Feature Tests if for some reason a test cannot be implemented in Cypress due to any limitations. You can read more about our [e2e testing](../types/e2e-tests.md). 10 | 11 | ::: 12 | 13 | Feature tests are tests from the perspective of the end-user. 14 | 15 | In the Rails world, we sometimes refer to these as Feature or System tests. A 16 | tool called Capybara is included to help us simulate a human's actions inside of 17 | our tests. 18 | 19 | Generally, we are simulating what a user could do from their web browser and 20 | ensuring that the app behaves as intended. When a feature is heavily reliant on 21 | interaction from a user via the browser, it's a good idea to write automated 22 | Feature tests to uncover any bugs that might not be apparent from manual 23 | testing. 24 | 25 | It's important to note that Rails System tests can be fairly slow, so it's 26 | better to focus on testing core functionality or pieces of your code that you 27 | think might be prone to regressions. Another strategy we use to help us keep 28 | Feature tests fast is `:aggregate_failures`. `:aggregate_failures` allows us 29 | to make multiple assertions within a single test. The flag signals to Rspec to 30 | run each assertion and then report all of the failures back to us rather than 31 | just the first failure. This saves time because we only have to setup the 32 | conditions for the spec once and then make all of our assertions instead of 33 | setting up the conditions repeatedly for each individual assertion. 34 | 35 | Feature tests can be found in the directory `spec/system`. 36 | 37 | You can run all Feature tests with: 38 | 39 | ```shell 40 | bundle exec rspec spec/system 41 | ``` 42 | 43 | To run an individual file you can use: 44 | 45 | ```shell 46 | bundle exec rspec spec/system/user_views_a_reading_list_spec.rb 47 | ``` 48 | 49 | To run a specific test case you can use: 50 | 51 | ```shell 52 | bundle exec rspec spec/system/user_views_a_reading_list_spec.rb:10 53 | ``` 54 | 55 | where `10` is the line number of the test case that you want to execute. 56 | 57 | You can read the official guide 58 | [Testing Rails Applications](https://guides.rubyonrails.org/testing.html#system-testing) 59 | to learn more. 60 | 61 | ## Test Flags 62 | 63 | When creating tests using Rspec we have the ability to add flags to those tests 64 | that will signal to Rspec to run certain commands before, after, or around the 65 | test example. 66 | 67 | ### `js: true` Flag 68 | 69 | `js: true` indicates that we want the JavaScript on the page to be executed when 70 | the page is rendered, and a headless chrome instance will be initialized to do 71 | so (instead of the default 72 | [rack_test](https://github.com/teamcapybara/capybara#racktest) driver). 73 | 74 | If you are debugging a `js: true` spec and want to see the browser, you can set 75 | `HEADLESS=false` before running a spec: 76 | 77 | ```shell 78 | HEADLESS=false bundle exec rspec spec/app/system 79 | ``` 80 | -------------------------------------------------------------------------------- /docs/tests/types/frontend-tests.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Frontend Tests 6 | 7 | A Frontend test will test JavaScript code whether it be some logic in a pack file or a Preact component. 8 | 9 | The test code is located within the same directory as each component, inside a 10 | `__tests__` directory. 11 | 12 | ```shell 13 | $ tree app/javascript/article-form -L 1 14 | app/javascript/article-form 15 | ├── __tests__ 16 | └── articleForm.jsx 17 | ``` 18 | 19 | The testing library being used is [Jest](https://jestjs.io/). 20 | 21 | - For unit tests, use jest's [expect API](https://jestjs.io/docs/en/expect) 22 | - For integration tests, use 23 | [preact-testing-library](https://github.com/testing-library/preact-testing-library). 24 | 25 | ## Running Tests 26 | 27 | You can run those tests with: 28 | 29 | ```shell 30 | npm run test 31 | ``` 32 | 33 | or 34 | 35 | ```shell 36 | yarn test 37 | ``` 38 | 39 | Should you want to view only a single jest test, you can run: 40 | 41 | ```shell 42 | npx jest app/javascript/ 43 | ``` 44 | 45 | ## Running Tests in Watch Mode 46 | 47 | You can run frontend tests in watch mode by running one of the following 48 | commands: 49 | 50 | ```shell 51 | npm run test:watch 52 | ``` 53 | 54 | or 55 | 56 | ```shell 57 | yarn test:watch 58 | ``` 59 | 60 | In watch mode, after the first test run, jest provides several options for 61 | filtering tests. These filtering options are enhanced via the 62 | [jest-watch-typeahead](https://github.com/jest-community/jest-watch-typeahead/blob/master/README.md) 63 | watch plugin. It allows you to filter by test filename or test name. 64 | 65 | ![Screenshot of the jest watch menu](/img/docs/tests/tests-jest.png) 66 | 67 | ## Debugging a Test 68 | 69 | To troubleshoot any of your jest test files, add a debugger and run: 70 | 71 | ```shell 72 | node --inspect-brk node_modules/.bin/jest --watch --runInBand 73 | ``` 74 | 75 | You can read more about troubleshooting 76 | [here](https://jestjs.io/docs/en/troubleshooting). 77 | 78 | At the end of the test's execution, you will see the code coverage for the 79 | Preact components in our codebase. 80 | 81 | If tests require utility modules, create them in a `utilities` folder under the 82 | `__tests__` folder. Jest is configured to not treat the `utilities` folder as a 83 | test suite. 84 | 85 | You can also debug jest in your favorite editor. See the 86 | [Debugging](../../frontend/debugging.md) section of the frontend documentation. 87 | -------------------------------------------------------------------------------- /docs/tests/types/integration-tests.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Integration Tests 6 | 7 | An integration test is a test that measures the interaction of multiple systems 8 | or parts of your application. They are meant to aspects like the requests and responses coming back from the server, data that ahs changed, business objects that has implemented. We’re not looking to testpage interactivity here, thats can be found in the [end to end tests](../types/e2e-tests.md). 9 | 10 | Integration tests can be found in the directory `spec/requests`. 11 | 12 | You can run all integration tests with: 13 | 14 | ```shell 15 | bundle exec rspec spec/requests 16 | ``` 17 | 18 | To run an individual file you can use: 19 | 20 | ```shell 21 | bundle exec rspec spec/requests/stories_show_spec.rb 22 | ``` 23 | 24 | To run a specific test case you can use: 25 | 26 | ```shell 27 | bundle exec rspec spec/requests/stories_show_spec.rb:10 28 | ``` 29 | 30 | where `10` is the line number of the test case that you want to execute. 31 | -------------------------------------------------------------------------------- /docs/tests/types/unit-functional-tests.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Unit and Functional Tests 6 | 7 | A unit test is about testing a single function/method in isolation with all of 8 | its possible outputs. 9 | 10 | A functional test is about testing a single functionality, which can span 11 | multiple methods and a controller. 12 | 13 | Other common terms in Rails are "model tests," "controller tests," and others. 14 | 15 | You can find model tests in `spec/models`, controller tests in 16 | `spec/controllers` and additional functional tests in various directories within 17 | the `spec` directory. 18 | 19 | You can run all models tests, for example, with: 20 | 21 | ```shell 22 | bundle exec rspec spec/models 23 | ``` 24 | 25 | To run an individual file you can use, for example: 26 | 27 | ```shell 28 | bundle exec rspec spec/models/user_spec.rb 29 | ``` 30 | 31 | To run a specific test case you can use, for example: 32 | 33 | ```shell 34 | bundle exec rspec spec/models/user_spec.rb:10 35 | ``` 36 | 37 | where `10` is the line number of the test case that you want to execute. 38 | 39 | ## Testing Controllers 40 | 41 | Historically, it has been common to use Rspec to write tests for Rails 42 | controllers. This pattern isn't necessarily discouraged in the Forem codebase, 43 | but Rspec has introduced a more effective way to test controllers via Request 44 | Specs. 45 | 46 | Request specs test the actions on a controller across the entire stack, 47 | effectively acting as Integration Tests. You can read more about request specs 48 | in our documentation on [Integration Tests](integration-tests.md). 49 | 50 | You can read the official guide [Testing Rails Applications](https://guides.rubyonrails.org/testing.html) to 51 | learn more. 52 | -------------------------------------------------------------------------------- /docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 11 3 | --- 4 | 5 | # Troubleshooting 6 | 7 | :::important 8 | 9 | We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email [yo@forem.com](mailto:yo@forem.com). 10 | 11 | ::: 12 | 13 | ## Tests 14 | 15 | ### Connection timeout 16 | 17 | While running test cases, if you get an error message 18 | `postgresql connection timeout`, please re-run the tests by increasing the 19 | statement timeout, for example: 20 | 21 | ```shell 22 | STATEMENT_TIMEOUT=10000 bundle exec rspec 23 | ``` 24 | 25 | ## PostgreSQL 26 | 27 | ### How do I fix the Error `role "ec2-user" does not exist` on an AWS instance? 28 | 29 | After installing and configuring PostgreSQL on an AWS EC2 (or AWS Cloud9) 30 | instance and running `bin/setup`, this error could occur. 31 | 32 | To fix it, run the following two commands in a terminal (assuming your 33 | PostgreSQL user is named **postgres**): 34 | 35 | ``` 36 | sudo -u postgres createuser -s ec2-user 37 | sudo -u postgres createdb ec2-user 38 | ``` 39 | 40 | The first command creates the user **ec2-user** and the second one creates the 41 | database for this user because every user needs its database. Even if the first 42 | command fails, run the second command to create the missing database. 43 | 44 | ## CORS 45 | 46 | If you are experiencing CORS issues locally or need to display more information 47 | about the CORS headers, add the following variable to your `.env`: 48 | 49 | ```shell 50 | export DEBUG_CORS=true 51 | ``` 52 | -------------------------------------------------------------------------------- /docusaurus.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@docusaurus/types').DocusaurusConfig} */ 2 | module.exports = { 3 | title: 'Forem Docs', 4 | tagline: 'Forem\'s developer documentation', 5 | url: 'https://developers.forem.com', 6 | baseUrl: '/', 7 | onBrokenLinks: 'throw', 8 | onBrokenMarkdownLinks: 'warn', 9 | favicon: 'img/favicon.ico', 10 | organizationName: 'forem', // Usually your GitHub org/user name. 11 | projectName: 'forem-docs', // Usually your repo name. 12 | themeConfig: { 13 | algolia: { 14 | apiKey: process.env.ALGOLIA_SEARCH_KEY || 'development', 15 | appId: process.env.ALGOLIA_APP_ID || 'development', 16 | indexName: process.env.ALGOLIA_INDEX_NAME || 'development', 17 | }, 18 | navbar: { 19 | title: 'Home', 20 | logo: { 21 | alt: 'Forem', 22 | src: 'img/logo.svg', 23 | }, 24 | items: [ 25 | { 26 | type: 'doc', 27 | docId: 'intro', 28 | position: 'left', 29 | label: 'Docs', 30 | }, 31 | { 32 | to: '/api', 33 | position: 'left', 34 | label: 'API', 35 | }, 36 | { 37 | title: 'GitHub', 38 | href: 'https://github.com/forem/forem', 39 | label: 'GitHub', 40 | position: 'right', 41 | } 42 | ], 43 | }, 44 | footer: { 45 | style: 'dark', 46 | links: [ 47 | { 48 | title: 'Docs', 49 | items: [ 50 | { 51 | label: 'Introduction', 52 | to: '/', 53 | }, 54 | { 55 | label: 'API Docs', 56 | to: '/api' 57 | } 58 | ], 59 | }, 60 | { 61 | title: 'Community', 62 | items: [ 63 | { 64 | label: 'forem.dev', 65 | href: 'https://forem.dev', 66 | }, 67 | { 68 | label: 'DEV', 69 | href: 'https://dev.to', 70 | }, 71 | { 72 | label: 'GitHub', 73 | href: 'https://github.com/forem/forem', 74 | }, 75 | ], 76 | }, 77 | { 78 | title: 'Social', 79 | items: [ 80 | { 81 | label: 'Twitter', 82 | href: 'https://twitter.com/forem', 83 | }, 84 | { 85 | label: 'dev.to Twitter', 86 | href: 'https://twitter.com/thepracticaldev', 87 | }, 88 | ], 89 | }, 90 | ], 91 | copyright: `Copyright © ${new Date().getFullYear()} Forem, Inc.`, 92 | }, 93 | }, 94 | presets: [ 95 | [ 96 | '@docusaurus/preset-classic', 97 | { 98 | docs: { 99 | routeBasePath: '/', 100 | sidebarPath: require.resolve('./sidebars.js'), 101 | editUrl: 102 | 'https://github.com/forem/forem-docs/edit/main', 103 | }, 104 | // blog: { 105 | // showReadingTime: true, 106 | // // Please change this to your repo. 107 | // editUrl: 108 | // 'https://github.com/forem/forem-docs/edit/main', 109 | // }, 110 | theme: { 111 | customCss: require.resolve('./src/css/custom.css'), 112 | }, 113 | }, 114 | ], 115 | [ 116 | 'redocusaurus', 117 | { 118 | specs: [{ 119 | spec: 'api_v0.yml', 120 | route: '/api/v0' 121 | }, { 122 | spec: 'api_v1.json', 123 | route: '/api/v1' 124 | }] 125 | } 126 | ] 127 | ], 128 | }; 129 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "https://docs.forem.com/*" 3 | to = "https://developers.forem.com/:splat" 4 | status = 301 5 | force = true 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forem-docss", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "api-docs:lint": "spectral lint -F hint -v api_v0.yml" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "2.4.1", 19 | "@docusaurus/preset-classic": "2.4.1", 20 | "@mdx-js/react": "^1.6.22", 21 | "clsx": "^1.2.1", 22 | "prism-react-renderer": "^1.3.5", 23 | "react": "^17.0.2", 24 | "react-dom": "^17.0.2", 25 | "@stoplight/spectral-cli": "^6.3.0", 26 | "redocusaurus": "^1.3.0" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.5%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | module.exports = { 13 | // By default, Docusaurus generates a sidebar from the docs folder structure 14 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 15 | 16 | // But you can create a sidebar manually 17 | /* 18 | tutorialSidebar: [ 19 | { 20 | type: 'category', 21 | label: 'Tutorial', 22 | items: ['hello'], 23 | }, 24 | ], 25 | */ 26 | }; 27 | -------------------------------------------------------------------------------- /src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: #005b30; 11 | --ifm-code-font-size: 95%; 12 | --ifm-footer-background-color: #022830; 13 | } 14 | 15 | html[data-theme='dark'] { 16 | --ifm-color-primary: #FBC1F5; 17 | --ifm-background-color: #022830; 18 | --ifm-navbar-background-color: #022830; 19 | } 20 | 21 | .theme-doc-markdown a, 22 | .theme-doc-markdown a:hover { 23 | text-decoration: underline; 24 | } 25 | 26 | .footer { 27 | background-color: #012026; 28 | } 29 | 30 | .docusaurus-highlight-code-line { 31 | background-color: rgb(72, 77, 91); 32 | display: block; 33 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 34 | padding: 0 var(--ifm-pre-padding); 35 | } 36 | -------------------------------------------------------------------------------- /static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/.nojekyll -------------------------------------------------------------------------------- /static/img/docs/admin_feature_flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/admin_feature_flags.png -------------------------------------------------------------------------------- /static/img/docs/backend/add-newapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/add-newapp.png -------------------------------------------------------------------------------- /static/img/docs/backend/admin-auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/admin-auth.png -------------------------------------------------------------------------------- /static/img/docs/backend/app-secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/app-secret.png -------------------------------------------------------------------------------- /static/img/docs/backend/appleadmin-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/appleadmin-config.png -------------------------------------------------------------------------------- /static/img/docs/backend/callback-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/callback-url.png -------------------------------------------------------------------------------- /static/img/docs/backend/config-facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/config-facebook.png -------------------------------------------------------------------------------- /static/img/docs/backend/create-appid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/create-appid.png -------------------------------------------------------------------------------- /static/img/docs/backend/email-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/email-source.png -------------------------------------------------------------------------------- /static/img/docs/backend/everything-else.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/everything-else.png -------------------------------------------------------------------------------- /static/img/docs/backend/facebook-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/facebook-login.png -------------------------------------------------------------------------------- /static/img/docs/backend/github-keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/github-keys.png -------------------------------------------------------------------------------- /static/img/docs/backend/github-oauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/github-oauth.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-1.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-10.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-11.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-12.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-2.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-3.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-4.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-5.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-6.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-7.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-8.png -------------------------------------------------------------------------------- /static/img/docs/backend/google-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/google-9.png -------------------------------------------------------------------------------- /static/img/docs/backend/key-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/key-download.png -------------------------------------------------------------------------------- /static/img/docs/backend/key-registration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/key-registration.png -------------------------------------------------------------------------------- /static/img/docs/backend/metrics-datadog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/metrics-datadog.png -------------------------------------------------------------------------------- /static/img/docs/backend/pusher-apps-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/pusher-apps-key.png -------------------------------------------------------------------------------- /static/img/docs/backend/pusher-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/pusher-create.png -------------------------------------------------------------------------------- /static/img/docs/backend/pusher-keys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/pusher-keys.png -------------------------------------------------------------------------------- /static/img/docs/backend/service-id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/service-id.png -------------------------------------------------------------------------------- /static/img/docs/backend/service-registration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/service-registration.png -------------------------------------------------------------------------------- /static/img/docs/backend/settings-basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/settings-basic.png -------------------------------------------------------------------------------- /static/img/docs/backend/setup-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/setup-login.png -------------------------------------------------------------------------------- /static/img/docs/backend/siteconfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/siteconfig.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-account-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-account-info.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-account-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-account-setup.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-api-reasons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-api-reasons.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-apply-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-apply-account.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-createapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-createapp.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-descript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-descript.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-developer-terms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-developer-terms.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-email-request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-email-request.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-enable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-enable.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-env.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-privacy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-privacy.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-terms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-terms.png -------------------------------------------------------------------------------- /static/img/docs/backend/twitter-work.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/backend/twitter-work.png -------------------------------------------------------------------------------- /static/img/docs/creators/creators-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/creators/creators-banner.png -------------------------------------------------------------------------------- /static/img/docs/creators/creators-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/creators/creators-data.png -------------------------------------------------------------------------------- /static/img/docs/creators/creators-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/creators/creators-permissions.png -------------------------------------------------------------------------------- /static/img/docs/creators/creators-role-single.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/creators/creators-role-single.png -------------------------------------------------------------------------------- /static/img/docs/creators/creators-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/creators/creators-settings.png -------------------------------------------------------------------------------- /static/img/docs/frontend/debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/frontend/debugger.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/fork-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/fork-button.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-forem-login-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-forem-login-page.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-forem-server-terminal-session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-forem-server-terminal-session.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-github-cli-creat-pr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-github-cli-creat-pr.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-github-cli-login-ssh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-github-cli-login-ssh.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-github-cli-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-github-cli-login.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-ide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-ide.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-install-vscode-extensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-install-vscode-extensions.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-open-site-terminal-session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-open-site-terminal-session.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-prebuild.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-prebuild.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-preview-browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-preview-browser.png -------------------------------------------------------------------------------- /static/img/docs/gitpod/gitpod-pulling-container-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/gitpod/gitpod-pulling-container-image.png -------------------------------------------------------------------------------- /static/img/docs/tests/cypress-test-runner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/tests/cypress-test-runner.png -------------------------------------------------------------------------------- /static/img/docs/tests/tests-jest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docs/tests/tests-jest.png -------------------------------------------------------------------------------- /static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/docusaurus.png -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /static/img/tutorial/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/tutorial/docsVersionDropdown.png -------------------------------------------------------------------------------- /static/img/tutorial/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forem/forem-docs/f2f06af608ddb4f93a3f8ec5b17a984ffb0c30f6/static/img/tutorial/localeDropdown.png --------------------------------------------------------------------------------