{"id":26378096,"url":"https://github.com/dylanfisher/forest","last_synced_at":"2025-03-17T04:21:09.171Z","repository":{"id":49219266,"uuid":"78307551","full_name":"dylanfisher/forest","owner":"dylanfisher","description":"Forest, a Rails CMS ","archived":false,"fork":false,"pushed_at":"2025-01-28T02:06:15.000Z","size":37377,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-28T11:45:56.248Z","etag":null,"topics":["cms","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dylanfisher.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-07T22:33:31.000Z","updated_at":"2025-01-28T02:06:19.000Z","dependencies_parsed_at":"2024-02-09T19:49:47.345Z","dependency_job_id":"abffab22-b6e5-4103-8b7e-1701ef9dee31","html_url":"https://github.com/dylanfisher/forest","commit_stats":{"total_commits":1383,"total_committers":2,"mean_commits":691.5,"dds":0.000723065798987732,"last_synced_commit":"0939dfeeb68c8f6cdbf587f4af6c37289bb552f1"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dylanfisher%2Fforest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dylanfisher%2Fforest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dylanfisher%2Fforest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dylanfisher%2Fforest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dylanfisher","download_url":"https://codeload.github.com/dylanfisher/forest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243971180,"owners_count":20376784,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cms","rails","ruby"],"created_at":"2025-03-17T04:21:08.640Z","updated_at":"2025-03-17T04:21:09.147Z","avatar_url":"https://github.com/dylanfisher.png","language":"Ruby","readme":"# 🌲 Forest\nForest is a Rails CMS that makes creating and managing websites easy.\nIt draws inspiration from Wordpress' dashboard and makes it easy to upload images,\nmanage menus, create users with permissions, etc.\n\n## Generating a new app\n```\nrails new myapp --database=postgresql\n```\n\n## Installation\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'forest', git: 'https://github.com/dylanfisher/forest.git'\n```\n\nInstall gems\n```bash\n$ bundle\n```\n\nCreate database and run migrations\n```\n$ rails db:create\n```\n\nRun Forest install generator and follow post-install prompts\n```bash\n$ rails g forest:install\n```\n\nDelete the ApplicationRecord file to inherit from Forest's ApplicationRecord.\n\nDefine a `root_path` in your host app's routes file\n```ruby\nroot to: 'pages#show' # or whatever makes sense for your home page controller\n```\n\nAdd the blocks directory to Rails' autoload paths:\n\n```ruby\n# application.rb\nconfig.autoload_paths \u003c\u003c \"#{config.root}/app/models/blocks\"\n```\n\nYou may want to add the following code to make it easy to monkey patch Forest's classes:\n\n```ruby\n# application.rb\nconfig.to_prepare do\n  # Load application's model / class decorators\n  Dir.glob(File.join(File.dirname(__FILE__), \"../app/**/*_decorator*.rb\")) do |c|\n    Rails.configuration.cache_classes ? require(c) : load(c)\n  end\nend\n```\n\nAdditional instructions and deployment suggestions are outlined in the [Wiki](https://github.com/dylanfisher/forest/wiki).\n\nFor an example of a host app running Forest, view [github.com/dylanfisher/forest_blog](https://github.com/dylanfisher/forest_blog).\n\n## Features\nForest aims to include the following features out of the box.\n\n### Pages\nPages are schedulable and draftable. Each page is composed of a series of blocks that\na user can use to create dynamic page layouts. Additional blocks are easy to develop using regular Ruby\nclasses and methods. Forest doesn't use any custom DSL.\n\n### Media Browser\nAn intuitive media browser, similar to Wordpress, is included by default and features multi-file drag and drop upload,\nand an easy to use modal interface for selecting associated files.\n\n### Menus\nA draggable, nestable interface for managing menus, similar to Wordpress.\n\n### Users\nUsers and user groups, a permissions system, and secure authentication using Devise.\n\n## Creating additional block types\nFirst, run the block type generator. Make sure to restart your server when generating new blocks.\n\n```\nrails generate forest:block TitleAndTextBlock title:string content:text\n```\n\n## Creating additional models backed by a CMS scaffold\n\n```\nrails generate forest:scaffold Project title:string media_item:references description:text\n```\n\nOptional arguments:\n\n`--skip_all` Equivalent to passing all of the below skip flags\n\n`--skip_public` Don't create public controller actions and views\n\n`--skip_blockable` Don't add blockable associations\n\n`--skip_statusable` Don't add a status column to the model\n\n`--skip_sluggable` Don't assume this model needs a slug\n\n## Custom SimpleForm inputs\nA number of custom SimpleForm inputs and components are baked into Forest: https://github.com/dylanfisher/forest/wiki/SimpleForm-inputs-\u0026-components\n\n## On Demand Video Transcoder\n\nA serverless on demand video transcoding workflow is built into Forest and relies on a few AWS services. Follow the setup guide for these\nservices at: https://github.com/dylanfisher/forest/wiki/AWS-Serverless-Video-Transcoding.\n\n## Video associations uploaded as Media Items\n```ruby\n# in your migration\nadd_reference :video_blocks, :video, foreign_key: { to_table: :media_items }\n```\n\n```ruby\n# video_block.rb\nbelongs_to :video, class_name: 'MediaItem'\n```\n\n```erb\n\u003c%= f.association :media_item, label: 'Video', remote: { path: admin_media_items_path(videos: true) } %\u003e\n```\n\n## Custom error pages\nAdd this line to application.rb to tell your host app to use the router to handle errors.\n\n`config.exceptions_app = self.routes`\n\nDelete the error pages in /public to avoid collision with the custom error pages.\n\nForest's router will render the appropriate error template via `ErrorsController`. Override the placeholder views to customize the error page.\n\nTo test the errors in development, set the following config to false.\n\n```ruby\n# development.rb\nconfig.consider_all_requests_local = false\n```\n\n## Search\nForest makes it easy to integrate with Elasticsearch. Just include the `Searchable` concern in any ActiveRecord models that you want to be searchable.\n\nThen, override the SearchIndexManager method below to indicate which models should be indexed.\n\n```ruby\nSearchIndexManager.class_eval do\n  def self.indexed_models\n    [Page, BlogPost]\n  end\nend\n```\n\nIndex your documents using the tasks in forest_elasticsearch_tasks.rake. To rebuild the search index, you'd run:\n\n`bin/rails forest:elasticsearch:rebuild`\n\n## Customize Forest Options\n\nA number of configuration options, such as image derivative options, are set in `lib/forest.rb` and can be customized in an initializer in your host app:\n\n```ruby\n# Forest.setup do |config|\n#   config.image_derivative_large_options[:resize][:width] = 3000\n#   config.image_derivative_large_options[:resize][:height] = 3000\n#   config.image_derivative_large_options[:jpeg][:quality] = 90\n# end\n```\n\n## Removing Block Kinds\n\n1. Delete all files related to the block kind (`_show.html.erb`, `edit.html.erb`, etc.).\n1. Create a new migration to destroy existing database entries related to the block:\n    ```ruby\n      def up\n        BlockSlot.where(block_type: 'ImageBlock').delete_all\n        BlockKind.where(name: 'ImageBlock').delete_all\n\n        remove_foreign_key(:image_blocks, :media_items) if foreign_key_exists?(:image_blocks, :media_items)\n        drop_table :image_blocks, if_exists: true\n      end\n    ```\n\n## Primary Dependencies\nForest relies heavily on the following gems, software and frameworks:\n\n- bootstrap\n- cocoon\n- devise\n- has_scope\n- jquery\n- pagy\n- postgres\n- pundit\n- redcarpet\n- shrine\n- simpleform\n\n## TODO\n\nVersion 2\n- [ ] Update settings to support an optional default fallback value for situations where a client may forget to enter a value.\n- [ ] Update Pundit::NotAuthorizedError on hidden records to return 404, not 500\n- [ ] Fix common errors in URL, e.g. `app/web.1: [a5d5d477-3e7b-411f-85d6-414af80850f2] ActionView::Template::Error (unexpected ''' after '[:equal, \"'/page/some-page/'\"]'):`\n- [ ] Remove hidden `block_layout`, `block_record_type`, `block_kind_id` fields from `block_slot_fields` and instead apply these values in the controller for better security.\n- [ ] Add pundit policy scoped to admin namespace, e.g. `authorize([:admin, Post])` https://github.com/varvet/pundit#policy-namespacing\n- [ ] Replace authorize index actions with `policy_scope` and make sure policy scope is set up properly\n- [ ] Add ability to toggle each individual blocks hidden status\n- [ ] Disable choosing a non-image file in the image association\n- [ ] Add an easier to use API for creating blocks and associating them to records programmatically\n- [ ] Update shrine file content disposition for non-images so that files have a more accurate file name\n- [ ] Add Shrine keep_files plugin to prevent deletion of files except in production environment https://shrinerb.com/docs/plugins/keep_files\n- [ ] move admin helper methods to definition in Admin::ForestController to avoid helper methods on the frontend host app\n- [ ] Update media library drag and drop to use smaller image thumbnail before image is done processing\n- [ ] Update scaffold generator to work with namespaced models\n- [ ] Replace and/or document suckerpunch background job with sidekiq or similar more resilient solution\n- [ ] Show instances of block kinds in the /admin/block-kinds interface\n- [ ] Allow configuration of Shrine media upload to allow local file storage\n- [ ] Better media item input (allow editing caption in place, show point of interest, etc.)\n- [ ] Don't delete media items from S3 storage in dev environment, better Shrine configuration for accidentally deleting files from S3\n- [ ] Update site title and description for meta tags\n- [ ] Add ability to collapse blocks, and (optionally) allow setting default collapse state\n- [ ] Add a refresh button to media item modal to refresh with newly added images\n- [ ] Add media item preview icons for PDF/Video and other files. Reference Uppy's pdf icon preview for inspiration\n- [ ] Media library needs multi-image delete functionality\n- [ ] Can we add indexes to media item attachment_data jsonb column, e.g. to index if a derivative is present?\n- [ ] Add ability to redirect to a page association, not just a string field\n- [ ] Allow menu items to link to reference a page ID in addition to a page path\n- [ ] scope simpleform configuration in engine so that global config doesn't interfere with host app\n- [ ] change references to model names and other hard coded names to use I18n\n- [ ] model resource_description should use I18n\n- [ ] better pattern for defining custom status message via I18n\n- [ ] add method to duplicate records\n- [ ] versioning option for data associated pages, settings, menus, etc. Version diffing ala wordpress.\n- [ ] better page versioning, page history navigation, restore content blocks with page version\n- [ ] email confirmations for user related actions\n- [ ] more robust and flexible permissions system via pundit\n- [ ] replace glyphicon with bootstrap 4 icons\n- [ ] tests\n- [ ] streamlined javascript event triggers namespaced to forest, e.g. `forest:block-slot-after-insert`\n- [ ] potentially refactor how settings are stored, how a setting's value type is inferred, how they are imported, and potentially add a reference to other models so that a model's settings can be displayed when editing a record.\n- [ ] add description to settings admin index view\n- [ ] add an easy way to add various metadata and settings to a page without having to add new columns to the `Page` record\n\nBlocks\n- [ ] refactor block partial to use helper for possible performance gains\n- [ ] add a duplicate block button to make adding additional blocks of the same type quicker and easier\n- [ ] add ability to drag certain content types, e.g. media items, between blocks\n- [ ] minimum and maximum blocks per layout\n\nMedia Gallery\n- [ ] search and replace all legacy references of attachment with new attachment_url method e.g. search for `.attachment.`\n- [ ] add drag and drop upload to media item edit view\n- [ ] add direct to S3 file upload for large files like video, that otherwise look like they timeout on heroku\n- [ ] ability to categorize media items on the backend, e.g. different directories of images\n- [ ] document the new requirements for using the media library, e.g. create the CloudFormation stack, add a background job library to host app's gemfile\n\n## Contributing\nIf you are interested in contributing to this project please [get in touch](mailto:hi@dylanfisher.com).\n\n## License\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdylanfisher%2Fforest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdylanfisher%2Fforest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdylanfisher%2Fforest/lists"}