diff --git a/.cloud66/deploy_hooks.yml b/.cloud66/deploy_hooks.yml deleted file mode 100644 index 220513e7e..000000000 --- a/.cloud66/deploy_hooks.yml +++ /dev/null @@ -1,51 +0,0 @@ -default: &default - after_bundle: - - source: /.cloud66/scripts/yarn_install.sh - destination: /tmp/yarn_install.sh - target: rails - execute: true - run_on: all_servers - - source: /.cloud66/scripts/clobber_assets.sh - destination: /tmp/clobber_assets.sh - target: rails - execute: true - apply_during: deploy_only - run_on: all_servers - after_rails: - - source: /.cloud66/scripts/db_seed.sh - destination: /tmp/db_seed.sh - target: rails - execute: true - run_on: single_server - apply_during: build_only - sudo: true - - source: /.cloud66/scripts/reseed_core_plugin.sh - destination: /tmp/reseed_core_plugin.sh - target: rails - execute: true - - source: /.cloud66/scripts/rebuild_indexes.sh - destination: /tmp/rebuild_indexes.sh - target: rails - execute: true - - command: cd $STACK_PATH && bundle exec pumactl -P /tmp/web_server.pid restart || true - target: rails - sudo: true - run_on: all_servers - -development: - <<: *default - -staging: - <<: *default - -production: - <<: *default - after_rails: - - source: /.cloud66/scripts/rebuild_indexes.sh - destination: /tmp/rebuild_indexes.sh - target: rails - execute: true - - command: cd $STACK_PATH && bundle exec pumactl -P /tmp/web_server.pid restart || true - target: rails - sudo: true - run_on: all_servers diff --git a/.cloud66/manifest.yml b/.cloud66/manifest.yml deleted file mode 100644 index 4f3584d37..000000000 --- a/.cloud66/manifest.yml +++ /dev/null @@ -1,104 +0,0 @@ -development: - load_balancer: - configuration: - httpchk: /users/password/new - rails: - configuration: - ruby_version: 2.4.2 - server: - unique_name: web - vendor: aws - region: us-east-1 - size: m3.medium - postgresql: - configuration: - version: 9.6.5 - server: - unique_name: db - vendor: aws - region: us-east-1 - size: m3.medium - elasticsearch: - configuration: - version: 5.6.2 - server: - unique_name: search - vendor: aws - region: us-east-1 - size: m3.medium - redis: - configuration: - version: 4.0.2 - server: - unique_name: cache - vendor: aws - region: us-east-1 - size: m3.medium - -staging: - rails: - configuration: - ruby_version: 2.4.2 - server: - unique_name: web - vendor: aws - region: us-east-1 - size: m3.medium - postgresql: - configuration: - version: 9.6.5 - server: - unique_name: db - vendor: aws - region: us-east-1 - size: m3.medium - elasticsearch: - configuration: - version: 5.6.2 - server: - unique_name: search - vendor: aws - region: us-east-1 - size: m3.medium - redis: - configuration: - version: 4.0.2 - server: - unique_name: cache - vendor: aws - region: us-east-1 - size: m3.medium - -production: - rails: - configuration: - ruby_version: 2.4.2 - server: - unique_name: web - vendor: aws - region: us-east-1 - size: c4.xlarge - postgresql: - configuration: - version: 9.6.5 - server: - unique_name: db - vendor: aws - region: us-east-1 - size: r3.xlarge - elasticsearch: - configuration: - version: 5.6.2 - server: - unique_name: search - vendor: aws - region: us-east-1 - size: r3.2xlarge - redis: - configuration: - version: 4.0.2 - server: - unique_name: cache - vendor: aws - region: us-east-1 - size: r3.xlarge diff --git a/.cloud66/scripts/clobber_assets.sh b/.cloud66/scripts/clobber_assets.sh deleted file mode 100644 index e52ad8b20..000000000 --- a/.cloud66/scripts/clobber_assets.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -cd $STACK_BASE/releases/$(ls -1 -t $STACK_BASE/releases/ | head -n1) - -echo "Executing clobber_assets in:" -pwd - -rm -rf public/assets/* app/assets/webpack/* diff --git a/.cloud66/scripts/db_seed.sh b/.cloud66/scripts/db_seed.sh deleted file mode 100644 index 869d9a2f9..000000000 --- a/.cloud66/scripts/db_seed.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -cd $STACK_PATH - -bundle exec rake db:seed diff --git a/.cloud66/scripts/rebuild_indexes.sh b/.cloud66/scripts/rebuild_indexes.sh deleted file mode 100644 index a42353f98..000000000 --- a/.cloud66/scripts/rebuild_indexes.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -cd $STACK_PATH - -echo "Executing rebuild_indexes in:" -pwd - -bundle exec rake cortex:rebuild_indexes diff --git a/.cloud66/scripts/reseed_core_plugin.sh b/.cloud66/scripts/reseed_core_plugin.sh deleted file mode 100644 index 784972f69..000000000 --- a/.cloud66/scripts/reseed_core_plugin.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -cd $STACK_PATH - -echo "Executing reseed_core_plugin in:" -pwd - -bundle exec rake cortex:core:db:reseed diff --git a/.cloud66/scripts/yarn_install.sh b/.cloud66/scripts/yarn_install.sh deleted file mode 100644 index b1ad87c83..000000000 --- a/.cloud66/scripts/yarn_install.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -cd $STACK_BASE/releases/$(ls -1 -t $STACK_BASE/releases/ | head -n1) -yarn install > /tmp/yarninstall.log diff --git a/.env.example b/.env.example deleted file mode 100644 index 74d0b6cac..000000000 --- a/.env.example +++ /dev/null @@ -1,91 +0,0 @@ -# Replace with `rake secret` -APP_SECRET= -DEVISE_SECRET= - -# -# Asset Host Configuration -# -HOST_ALIAS= -ASSET_HOST_URL= - -# S3 -S3_ACCESS_KEY_ID= -S3_SECRET_ACCESS_KEY= -S3_BUCKET_NAME= -S3_REGION= - -# -# GraphQL -# -GRAPHQL_URL=http://localhost:4000/graphql - -# -# Monitoring -# -NEW_RELIC_LICENSE_KEY= -SENTRY_RAVEN_DSN= - -# Apollo Engine -APOLLO_ENGINE_PROXY_API_KEY=service:YOUR_ENGINE_API_KEY -APOLLO_ENGINE_PROXY_LOGGING_LEVEL=INFO -APOLLO_ENGINE_PROXY_ORIGIN_HTTP_URL=http://localhost:3000/graphql -APOLLO_ENGINE_PROXY_FRONTEND_HOST=localhost -APOLLO_ENGINE_PROXY_FRONTEND_PORT=4000 -APOLLO_ENGINE_PROXY_FRONTEND_ENDPOINT=/graphql - -# -# Email Configuration -# -SMTP_ADDRESS= -SMTP_PORT=587 -SMTP_SENDER_DOMAIN=cortexcms.org -SMTP_SENDER_ADDRESS=noreply@cortexcms.org -SMTP_USERNAME= -SMTP_PASSWORD= -SMTP_STARTTLS=true - -# -# CORS Configuration -# - -# CSV allowed origins. Default is '*' -CORS_ALLOWED_ORIGINS= - -# Optional regular expression allowed origin(s) -CORS_ALLOWED_ORIGINS_REGEX= - -# -# External Services -# - -# Vendor/3rd Party Script Configuration -GOOGLE_ANALYTICS_ID= -QUALTRICS_ID= - -# -# Database Configuration -# -#DATABASE_NAME= -#DATABASE_USERNAME= -#DATABASE_PASSWORD= -#DATABASE_HOST= -#DATABASE_PORT= - -# -# Caching -# -REDIS_NAMESPACE=cortex_dev -CACHE_URL=redis://localhost:6379/0/cache -SESSION_STORE_URL=redis://localhost:6379/0/cache - -# -# Search -# -ELASTICSEARCH_ADDRESS=localhost:9200 - -# -# Admin Interface -# - -# News Feed / Release notes -CORTEX_NEWS_FEED_TENANT= diff --git a/.gitignore b/.gitignore index 184d11b02..5e080eafd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,23 +4,15 @@ # or operating system, you probably want to add a global ignore instead: # git config --global core.excludesfile '~/.gitignore_global' +# Build Artifacts +pkg/ +node_package/lib +**.gem + # Ignore bundler config. /.bundle /vendor/bundle -/public/assets/ -/public/uploads/ - -# Ignore the default SQLite database. -/log/* -/tmp/* -!/log/.keep -!/tmp/.keep - -# Ignore all logfiles and tempfiles. -/log/*.log -/tmp - # Debug .byebug_history @@ -40,28 +32,51 @@ .sass-cache capybara-*.html .powrc -/public/system /coverage/ /spec/tmp /spec/examples.txt **.orig rerun.txt pickle-email-*.html -.env -/.cloud66/stack.json -# these should all be checked in to normalise the environment: -# Gemfile.lock, .ruby-version, .ruby-gemset +# rspec failure tracking +.rspec_status -# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: +# Environment .rvmrc +.ruby-version +.env # Documentation Artifacts erd.pdf +# +# Dummy Spec App +# + +spec/dummy/public/assets/ +spec/dummy/public/uploads/ +spec/dummy/public/system/ + +# Ignore the default SQLite database. +spec/dummy/db/*.sqlite3 +spec/dummy/db/*.sqlite3-journal +spec/dummy/log/*.log +spec/dummy/node_modules/ +spec/dummy/yarn-error.log +spec/dummy/storage/ +spec/dummy/tmp/ + +# Ignore all logfiles and tempfiles. +/spec/dummy/log/* +/spec/dummy/tmp/* +!/spec/dummy/log/.keep +!/spec/dummy/tmp/.keep + # Webpacker Pipeline npm-debug.log* -/public/packs -/public/packs-test +/spec/dummy/public/packs +/spec/dummy/public/packs-test node_modules /yarn-error.log +/spec/dummy/yarn-error.log diff --git a/.npmignore b/.npmignore new file mode 100644 index 000000000..819506f52 --- /dev/null +++ b/.npmignore @@ -0,0 +1,34 @@ +.gitignore +.npmignore +.editorconfig +.github +CHANGELOG.md +Gemfile +README.md +app +coverage +docs +examples +node_modules +*.gemspec +*.gem +Dockerfile_tests +Gemfile +Gemfile.lock +Rakefile +yarn.lock +bin +docker-compose.yml +etc +lib +config +bin +db +app +rakelib +ruby-lint.yml +test +spec +node_modules +tmp +gen-examples diff --git a/.rspec b/.rspec index 83e16f804..34c5164d9 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1,3 @@ +--format documentation --color --require spec_helper diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 8e8299dcc..000000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.4.2 diff --git a/Brewfile b/Brewfile deleted file mode 100644 index 424822a5f..000000000 --- a/Brewfile +++ /dev/null @@ -1,8 +0,0 @@ -readline -postgresql -redis -Caskroom/cask/java -elasticsearch -phantomjs -imagemagick -ghostscript diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..f2e9916f9 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at toastercup@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/Gemfile b/Gemfile index 4f9c7b50a..f3ef22524 100644 --- a/Gemfile +++ b/Gemfile @@ -1,110 +1,23 @@ source 'https://rubygems.org' -ruby '2.4.2' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } -git_source(:github) do |repo_name| - repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") - "https://github.com/#{repo_name}.git" -end - -# Server -gem 'puma', '~> 3.10.0' - -# Rails -gem 'rails', '~> 5.1.4' - -# API -gem 'graphql', '~> 1.7.5' -gem 'graphiql-rails', '~> 1.4.7' -gem 'apollo-tracing', '~> 1.3.0' - -# Cortex-specific -gem 'cortex-exceptions', '= 0.0.4' -gem 'cortex-plugins-core', '= 2.1.1' - -# Service Layer -gem 'dry-types', '~> 0.12.2' -gem 'dry-struct', '~> 0.4.0' -gem 'dry-transaction', '~> 0.10.2' - -# Authentication -gem 'devise', '~> 4.3.0' - -# Authorization -gem 'rolify', '~> 5.1.0' -gem 'pundit', '~> 1.1.0' - -# Data -gem 'awesome_nested_set', '~> 3.1.3' -gem 'bcrypt', '~> 3.1.11' -gem 'kaminari', '~> 1.1.1' -gem 'elasticsearch-model', '~> 5.0' -gem 'elasticsearch-rails', '~> 5.0' -gem 'elasticsearch-dsl', '~> 0.1' -gem 'paranoia', '~> 2.4' -gem 'pg', '~> 0.21.0' -gem 'redis-rails', '~> 5.0' -gem 'pomona', '~> 0.7' -gem 'transitions', '~> 1.2', require: %w(transitions active_model/transitions) +# Declare your gem's dependencies in cortex.gemspec. +# Bundler will treat runtime dependencies like base dependencies, and +# development dependencies will be added by default to the :development group. +gemspec -# Middleware -gem 'rack-cors', '~> 1.0.2', require: 'rack/cors' +# Declare any dependencies that are still in development here instead of in +# your gemspec. These might include edge Rails or gems from your path or +# Git. Remember to move these dependencies to your gemspec before releasing +# your gem to rubygems.org. -# Utility -gem 'hashie', '~> 3.5.6' -gem 'hashr', '~> 2.0.1' -gem 'mimemagic', '~> 0.3.2' -gem 'addressable', '~> 2.5.2' -gem 'json' -gem 'nokogiri' - -# Jobs -gem 'sidekiq', '~> 5.0.5' -gem 'sidekiq-failures', '~> 1.0.0' -gem 'sinatra', '~> 2.0.0', require: false - -# Pipeline -gem 'sprockets-rails', '3.2.1', require: 'sprockets/railtie' -gem 'sprockets', '3.7.1' -gem 'uglifier', '~> 3.2.0' -gem 'non-stupid-digest-assets', '~> 1.0.9' - -# View -gem 'haml', '~> 5.0' -gem 'cells', '~> 4.1.7' -gem 'cells-rails', '~> 0.0.8' -gem 'cells-haml', '~> 0.0.10' -gem 'breadcrumbs_on_rails', '~> 3.0.1' - -# Style -gem 'sass-rails', '~> 5.0' -gem 'bourbon', '~> 4.3' -gem 'font-awesome-sass', '~> 4.7.0' -gem 'material_design_lite-sass', '~> 1.3.0' - -# JavaScript -gem 'react_on_rails', '9.0.3' -gem 'mini_racer', platforms: :ruby -gem 'webpacker' -gem 'gon', '~> 6.2.0' -#gem 'turbolinks', '~> 5.0.1' -gem 'jquery-rails', '~> 4.3.1' -#gem 'jquery-turbolinks', '~> 2.1' -gem 'jquery-ui-rails', '~> 6.0.1' -gem 'bootstrap-tagsinput-rails', '~> 0.4.2' -gem 'dialog-polyfill-rails', '~> 0.4.5' - -# Feature Flagging -gem 'flipper', '~> 0.11' -gem 'flipper-ui', '~> 0.11' -gem 'flipper-active_record', '~> 0.11' +# To use a debugger +# gem 'byebug', group: [:development, :test] group :development, :test do # Environment gem 'dotenv-rails', require: 'dotenv/rails-now' - # Cache/Sidekiq - gem 'redis-namespace' - # Debug gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'pry-rails' @@ -112,24 +25,19 @@ group :development, :test do gem 'pry-remote' gem 'pry-stack_explorer' - # Specs + # Specs/Helpers gem 'rspec-rails' gem 'guard-rspec', require: false gem 'database_cleaner' + gem 'factory_girl_rails', '~> 4.8' # TODO: upgrade to factory_bot + gem 'faker', '~> 1.8' + gem 'phantomjs', '~> 2.1' + gem 'jasmine-rails', '~> 0.14' # Documentation gem 'rails-erd' end -group :development do - # Debug - gem 'better_errors' - gem 'binding_of_caller' - - # Misc - gem 'listen', '>= 3.0.5', '< 3.2' -end - group :test do # Rspec gem 'json_spec', '~> 1.1' @@ -156,22 +64,3 @@ group :test do # Data gem 'elasticsearch-extensions', '~> 0.0.26' end - -group :test, :development do - gem 'factory_girl_rails', '~> 4.8' # TODO: upgrade to factory_bot - gem 'faker', '~> 1.8' - gem 'phantomjs', '~> 2.1' - gem 'jasmine-rails', '~> 0.14' -end - -group :staging, :production do - # Monitoring - gem 'newrelic_rpm' - gem 'sentry-raven' - - # Performance - gem 'bootscale', require: false -end - -# Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/Gemfile.lock b/Gemfile.lock index 7394e47c2..92cb9bcd5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,44 @@ +PATH + remote: . + specs: + cortex (0.1.0) + addressable (~> 2.5.2) + awesome_nested_set (~> 3.1.3) + breadcrumbs_on_rails (~> 3.0.1) + cells (~> 4.1.7) + cells-haml (~> 0.0.10) + cells-rails (~> 0.0.8) + cortex-exceptions (= 0.0.4) + devise (~> 4.3.0) + dry-struct (~> 0.4.0) + dry-transaction (~> 0.10.2) + dry-types (~> 0.12.2) + elasticsearch-dsl (~> 0.1) + elasticsearch-model (~> 5.0) + elasticsearch-rails (~> 5.0) + flipper (~> 0.11) + flipper-active_record (~> 0.11) + flipper-ui (~> 0.11) + gon (~> 6.2.0) + graphiql-rails (~> 1.4.7) + graphql (~> 1.7.5) + haml (~> 5.0) + hashie (~> 3.5.6) + json + mimemagic (~> 0.3.2) + mini_racer + nokogiri + paranoia (~> 2.4) + pomona (~> 0.7) + pundit (~> 1.1.0) + rack-cors (~> 1.0.2) + rails (~> 5.1.4) + react_on_rails (= 9.0.3) + rolify (~> 5.1.0) + sass-rails (~> 5.0) + transitions (~> 1.2) + webpacker + GEM remote: https://rubygems.org/ specs: @@ -41,49 +82,22 @@ GEM addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) ansi (1.5.0) - apollo-tracing (1.3.0) - graphql (>= 1.6.0, < 2) arel (8.0.0) - autoprefixer-rails (7.1.6) - execjs awesome_nested_set (3.1.3) activerecord (>= 4.0.0, < 5.2) - aws-partitions (1.35.0) - aws-sdk-core (3.7.0) - aws-partitions (~> 1.0) - aws-sigv4 (~> 1.0) - jmespath (~> 1.0) - aws-sdk-kms (1.3.0) - aws-sdk-core (~> 3) - aws-sigv4 (~> 1.0) - aws-sdk-s3 (1.6.0) - aws-sdk-core (~> 3) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.0) - aws-sigv4 (1.0.2) bcrypt (3.1.11) - better_errors (2.4.0) - coderay (>= 1.0.0) - erubi (>= 1.0.0) - rack (>= 0.9.0) - binding_of_caller (0.7.3) + binding_of_caller (0.8.0) debug_inspector (>= 0.0.1) - bootscale (0.7.0) - bootstrap-tagsinput-rails (0.4.2.1) - railties (>= 3.1) - bourbon (4.3.4) - sass (~> 3.4) - thor (~> 0.19) breadcrumbs_on_rails (3.0.1) builder (3.2.3) byebug (9.1.0) - capybara (2.16.0) + capybara (2.17.0) addressable mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) - xpath (~> 2.0) + xpath (>= 2.0, < 4.0) cells (4.1.7) declarative-builder (< 0.2.0) declarative-option (< 0.2.0) @@ -105,20 +119,6 @@ GEM concurrent-ruby (1.0.5) connection_pool (2.2.1) cortex-exceptions (0.0.4) - cortex-plugins-core (2.1.1) - aws-sdk-s3 (~> 1.5) - cells (~> 4.1) - cells-haml (~> 0.0) - cells-rails (~> 0.0) - fastimage (~> 2.1) - image_optim (~> 0.25) - image_optim_pack (~> 0.5) - image_processing (~> 0.4) - jsonb_accessor (~> 1.0) - mimemagic (~> 0.3) - mini_magick (~> 4.8) - rails (>= 5) - shrine (~> 2.7) crass (1.0.3) database_cleaner (1.6.2) debug_inspector (0.0.3) @@ -131,27 +131,25 @@ GEM railties (>= 4.1.0, < 5.2) responders warden (~> 1.2.3) - dialog-polyfill-rails (0.4.5.1) diff-lcs (1.3) docile (1.1.5) dotenv (2.2.1) dotenv-rails (2.2.1) dotenv (= 2.2.1) railties (>= 3.2, < 5.2) - down (4.1.1) dry-configurable (0.7.0) concurrent-ruby (~> 1.0) dry-container (0.6.0) concurrent-ruby (~> 1.0) dry-configurable (~> 0.1, >= 0.1.3) - dry-core (0.4.1) + dry-core (0.4.2) concurrent-ruby (~> 1.0) dry-equalizer (0.2.0) dry-logic (0.4.2) dry-container (~> 0.2, >= 0.2.6) dry-core (~> 0.2) dry-equalizer (~> 0.2) - dry-matcher (0.6.0) + dry-matcher (0.7.0) dry-monads (0.4.0) dry-core (~> 0.3, >= 0.3.3) dry-equalizer @@ -185,11 +183,11 @@ GEM oj patron ruby-prof - elasticsearch-model (5.0.1) + elasticsearch-model (5.0.2) activesupport (> 3) elasticsearch (~> 5) hashie - elasticsearch-rails (5.0.1) + elasticsearch-rails (5.0.2) elasticsearch-transport (5.0.4) faraday multi_json @@ -200,44 +198,39 @@ GEM erubi (1.7.0) erubis (2.7.0) execjs (2.7.0) - exifr (1.3.2) factory_girl (4.9.0) activesupport (>= 3.0.0) factory_girl_rails (4.9.0) factory_girl (~> 4.9.0) railties (>= 3.0.0) - faker (1.8.4) - i18n (~> 0.5) - faraday (0.13.1) + faker (1.8.7) + i18n (>= 0.7) + faraday (0.14.0) multipart-post (>= 1.2, < 3) - fastimage (2.1.0) ffi (1.9.18) - flipper (0.11.0) - flipper-active_record (0.11.0) + flipper (0.12.2) + flipper-active_record (0.12.2) activerecord (>= 3.2, < 6) - flipper (~> 0.11.0) - flipper-ui (0.11.0) + flipper (~> 0.12.2) + flipper-ui (0.12.2) erubis (~> 2.7.0) - flipper (~> 0.11.0) + flipper (~> 0.12.2) rack (>= 1.4, < 3) rack-protection (>= 1.5.3, < 2.1.0) - font-awesome-sass (4.7.0) - sass (>= 3.2) formatador (0.2.5) - fspath (3.1.0) globalid (0.4.1) activesupport (>= 4.2.0) gon (6.2.0) actionpack (>= 3.0) multi_json request_store (>= 1.0) - graphiql-rails (1.4.7) + graphiql-rails (1.4.8) rails - graphql (1.7.6) - guard (2.14.1) + graphql (1.7.8) + guard (2.14.2) formatador (>= 0.2.4) listen (>= 2.7, < 4.0) - lumberjack (~> 1.0) + lumberjack (>= 1.0.12, < 2.0) nenv (~> 0.1) notiffany (~> 0.0) pry (>= 0.9.12) @@ -259,66 +252,30 @@ GEM haml (5.0.4) temple (>= 0.8.0) tilt - hashie (3.5.6) - hashr (2.0.1) + hashie (3.5.7) htmlentities (4.3.4) - i18n (0.9.1) + i18n (0.9.3) concurrent-ruby (~> 1.0) ice_nine (0.11.2) - image_optim (0.26.0) - exifr (~> 1.2, >= 1.2.2) - fspath (~> 3.0) - image_size (~> 1.5) - in_threads (~> 1.3) - progress (~> 3.0, >= 3.0.1) - image_optim_pack (0.5.0.20171101) - fspath (>= 2.1, < 4) - image_optim (~> 0.19) - image_processing (0.4.5) - image_size (1.5.0) - in_threads (1.5.0) inflecto (0.0.2) - jasmine (2.8.0) - jasmine-core (>= 2.8.0, < 3.0.0) + jasmine (2.9.0) + jasmine-core (>= 2.9.0, < 3.0.0) phantomjs rack (>= 1.2.1) rake - jasmine-core (2.8.0) + jasmine-core (2.9.1) jasmine-rails (0.14.7) jasmine-core (>= 1.3, < 3.0) phantomjs (>= 1.9) railties (>= 3.2.0) sprockets-rails - jmespath (1.3.1) - jquery-rails (4.3.1) - rails-dom-testing (>= 1, < 3) - railties (>= 4.2.0) - thor (>= 0.14, < 2.0) - jquery-ui-rails (6.0.1) - railties (>= 3.2.16) json (2.1.0) json_spec (1.1.5) multi_json (~> 1.0) rspec (>= 2.0, < 4.0) - jsonb_accessor (1.0.0) - activerecord (>= 5.0) - activesupport (>= 5.0) - pg (>= 0.18.1) - kaminari (1.1.1) - activesupport (>= 4.1.0) - kaminari-actionview (= 1.1.1) - kaminari-activerecord (= 1.1.1) - kaminari-core (= 1.1.1) - kaminari-actionview (1.1.1) - actionview - kaminari-core (= 1.1.1) - kaminari-activerecord (1.1.1) - activerecord - kaminari-core (= 1.1.1) - kaminari-core (1.1.1) launchy (2.4.3) addressable (~> 2.3) - libv8 (5.9.211.38.1) + libv8 (6.3.292.48.1) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -329,46 +286,36 @@ GEM lumberjack (1.0.12) mail (2.7.0) mini_mime (>= 0.1.1) - material_design_lite-sass (1.3.0.1) - autoprefixer-rails (>= 6.5) - sass (>= 3.3) metaclass (0.0.4) method_source (0.8.2) mimemagic (0.3.2) - mini_magick (4.8.0) mini_mime (1.0.0) mini_portile2 (2.3.0) - mini_racer (0.1.14) - libv8 (~> 5.9) - minitest (5.10.3) + mini_racer (0.1.15) + libv8 (~> 6.3) + minitest (5.11.2) mocha (1.3.0) metaclass (~> 0.0.1) - multi_json (1.12.2) + multi_json (1.13.1) multipart-post (2.0.0) - mustermann (1.0.1) nenv (0.3.0) - newrelic_rpm (4.6.0.338) - nio4r (2.1.0) + nio4r (2.2.0) nokogiri (1.8.1) mini_portile2 (~> 2.3.0) - non-stupid-digest-assets (1.0.9) - sprockets (>= 2.0) notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) - oj (3.3.9) + oj (3.4.0) orm_adapter (0.5.0) paranoia (2.4.0) activerecord (>= 4.0, < 5.2) patron (0.10.0) - pg (0.21.0) phantomjs (2.1.1.0) - poltergeist (1.16.0) + poltergeist (1.17.0) capybara (~> 2.1) cliver (~> 0.3.1) websocket-driver (>= 0.2.0) pomona (0.7.0) - progress (3.4.0) pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -384,16 +331,15 @@ GEM binding_of_caller (>= 0.7) pry (>= 0.9.11) public_suffix (3.0.1) - puma (3.10.0) pundit (1.1.0) activesupport (>= 3.0.0) rack (2.0.3) rack-cors (1.0.2) rack-protection (2.0.0) rack - rack-proxy (0.6.2) + rack-proxy (0.6.3) rack - rack-test (0.7.0) + rack-test (0.8.2) rack (>= 1.0, < 3) rails (5.1.4) actioncable (= 5.1.4) @@ -436,25 +382,8 @@ GEM rails (>= 3.2) rainbow (~> 2.2) redis (4.0.1) - redis-actionpack (5.0.2) - actionpack (>= 4.0, < 6) - redis-rack (>= 1, < 3) - redis-store (>= 1.1.0, < 2) - redis-activesupport (5.0.4) - activesupport (>= 3, < 6) - redis-store (>= 1.3, < 2) - redis-namespace (1.6.0) - redis (>= 3.0.4) - redis-rack (2.0.3) - rack (>= 1.5, < 3) - redis-store (>= 1.2, < 2) - redis-rails (5.0.2) - redis-actionpack (>= 5.0, < 6) - redis-activesupport (>= 5.0, < 6) - redis-store (>= 1.2, < 2) - redis-store (1.4.1) - redis (>= 2.2, < 5) - request_store (1.3.2) + request_store (1.4.0) + rack (>= 1.4) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) @@ -463,7 +392,7 @@ GEM rspec-core (~> 3.7.0) rspec-expectations (~> 3.7.0) rspec-mocks (~> 3.7.0) - rspec-core (3.7.0) + rspec-core (3.7.1) rspec-support (~> 3.7.0) rspec-expectations (3.7.0) diff-lcs (>= 1.2.0, < 2.0) @@ -471,7 +400,7 @@ GEM rspec-mocks (3.7.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.7.0) - rspec-rails (3.7.1) + rspec-rails (3.7.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) @@ -484,9 +413,9 @@ GEM sidekiq (>= 2.4.0) rspec-support (3.7.0) ruby-graphviz (1.2.3) - ruby-prof (0.16.2) + ruby-prof (0.17.0) ruby_dep (1.5.0) - sass (3.5.3) + sass (3.5.5) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -497,30 +426,19 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - sentry-raven (2.7.1) - faraday (>= 0.7.6, < 1.0) shellany (0.0.1) shoulda-matchers (3.1.2) activesupport (>= 4.0.0) - shrine (2.8.0) - down (~> 4.1) sidekiq (5.0.5) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) redis (>= 3.3.4, < 5) - sidekiq-failures (1.0.0) - sidekiq (>= 4.0.0) simplecov (0.15.1) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) - sinatra (2.0.0) - mustermann (~> 1.0) - rack (~> 2.0) - rack-protection (= 2.0.0) - tilt (~> 2.0) slop (3.6.0) sprockets (3.7.1) concurrent-ruby (~> 1.0) @@ -538,11 +456,9 @@ GEM tzinfo (1.2.4) thread_safe (~> 0.1) uber (0.1.0) - uglifier (3.2.0) - execjs (>= 0.3.0, < 3) warden (1.2.7) rack (>= 1.0) - webpacker (3.0.2) + webpacker (3.2.1) activesupport (>= 4.2) rack-proxy (>= 0.6.1) railties (>= 4.2) @@ -550,109 +466,41 @@ GEM websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) wisper (2.0.0) - xpath (2.1.0) - nokogiri (~> 1.3) + xpath (3.0.0) + nokogiri (~> 1.8) PLATFORMS ruby DEPENDENCIES - addressable (~> 2.5.2) - apollo-tracing (~> 1.3.0) - awesome_nested_set (~> 3.1.3) - bcrypt (~> 3.1.11) - better_errors - binding_of_caller - bootscale - bootstrap-tagsinput-rails (~> 0.4.2) - bourbon (~> 4.3) - breadcrumbs_on_rails (~> 3.0.1) byebug capybara - cells (~> 4.1.7) - cells-haml (~> 0.0.10) - cells-rails (~> 0.0.8) codeclimate-test-reporter - cortex-exceptions (= 0.0.4) - cortex-plugins-core (= 2.1.1) + cortex! database_cleaner - devise (~> 4.3.0) - dialog-polyfill-rails (~> 0.4.5) dotenv-rails - dry-struct (~> 0.4.0) - dry-transaction (~> 0.10.2) - dry-types (~> 0.12.2) - elasticsearch-dsl (~> 0.1) elasticsearch-extensions (~> 0.0.26) - elasticsearch-model (~> 5.0) - elasticsearch-rails (~> 5.0) email_spec factory_girl_rails (~> 4.8) faker (~> 1.8) - flipper (~> 0.11) - flipper-active_record (~> 0.11) - flipper-ui (~> 0.11) - font-awesome-sass (~> 4.7.0) - gon (~> 6.2.0) - graphiql-rails (~> 1.4.7) - graphql (~> 1.7.5) guard-jasmine (~> 2.1) guard-rspec - haml (~> 5.0) - hashie (~> 3.5.6) - hashr (~> 2.0.1) jasmine-core (~> 2.8) jasmine-rails (~> 0.14) - jquery-rails (~> 4.3.1) - jquery-ui-rails (~> 6.0.1) - json json_spec (~> 1.1) - kaminari (~> 1.1.1) - listen (>= 3.0.5, < 3.2) - material_design_lite-sass (~> 1.3.0) - mimemagic (~> 0.3.2) - mini_racer mocha (~> 1.3) - newrelic_rpm - nokogiri - non-stupid-digest-assets (~> 1.0.9) - paranoia (~> 2.4) - pg (~> 0.21.0) phantomjs (~> 2.1) poltergeist - pomona (~> 0.7) pry-nav pry-rails pry-remote pry-stack_explorer - puma (~> 3.10.0) - pundit (~> 1.1.0) - rack-cors (~> 1.0.2) - rails (~> 5.1.4) rails-erd - react_on_rails (= 9.0.3) - redis-namespace - redis-rails (~> 5.0) - rolify (~> 5.1.0) rspec-rails rspec-sidekiq (~> 3.0) - sass-rails (~> 5.0) - sentry-raven shoulda-matchers (~> 3.1) - sidekiq (~> 5.0.5) - sidekiq-failures (~> 1.0.0) simplecov - sinatra (~> 2.0.0) - sprockets (= 3.7.1) - sprockets-rails (= 3.2.1) timecop (~> 0.9) - transitions (~> 1.2) - tzinfo-data - uglifier (~> 3.2.0) - webpacker - -RUBY VERSION - ruby 2.4.2p198 BUNDLED WITH - 1.16.0 + 1.16.1 diff --git a/Procfile b/Procfile deleted file mode 100644 index d48e7fb13..000000000 --- a/Procfile +++ /dev/null @@ -1,3 +0,0 @@ -web: bundle exec puma -e $RACK_ENV -b unix:///tmp/web_server.sock --pidfile /tmp/web_server.pid -d -worker: bundle exec sidekiq -e $RAILS_ENV --config ./config/sidekiq.yml -custom_web: bundle exec puma -e $RACK_ENV -b unix:///tmp/web_server.sock --pidfile /tmp/web_server.pid -d diff --git a/Procfile.dev b/Procfile.dev deleted file mode 100644 index 9290db11a..000000000 --- a/Procfile.dev +++ /dev/null @@ -1,8 +0,0 @@ -# You can run these commands in separate shells instead of using foreman -web: bundle exec rails s -p 3000 - -# Next line runs a watch process with webpack to compile the changed files. -# When making frequent changes to client side assets, you will prefer building webpack assets -# upon saving rather than when you refresh your browser page. -worker: bundle exec sidekiq -e development --config ./config/sidekiq.yml -client: sh -c 'rm -rf public/packs/* || true && bundle exec rake react_on_rails:locale && bin/webpack -w' diff --git a/Procfile.dev-server b/Procfile.dev-server deleted file mode 100644 index 602fb4968..000000000 --- a/Procfile.dev-server +++ /dev/null @@ -1,12 +0,0 @@ -# You can run these commands in separate shells instead of using foreman -web: rails s -p 3000 - -# Next line runs the webpack-dev-server -# You can edit config/webpacker.yml to set HMR to true to see hot reloading. -# Note, hot and live reloading don't work with the default generator setup on top of -# the rails/webpacker Webpack config with server rendering. -# If you have server rendering enabled, modify the call to bin/webpack-dev-server line -# so you add `--inline=false` and then CSS is not inlined. -# Otherwise, you will have an error. If you want HMR and Server Rendering, see -# the example in the https://github.com/shakacode/react-webpack-rails-tutorial -client: sh -c 'rm -rf public/packs/* || true && bundle exec rake react_on_rails:locale && bin/webpack-dev-server' diff --git a/README.md b/README.md index da6395625..25849738c 100644 --- a/README.md +++ b/README.md @@ -198,8 +198,6 @@ Swagger Endpoints are available at [http://api.cbcortex.com/api/v1/swagger_doc.j Cortex's API utilizes [OAuth2](https://tools.ietf.org/html/rfc6749) for Authentication and Authorization. Client Credentials and Authorization Code [grant types](http://alexbilbie.com/2013/02/a-guide-to-oauth-2-grants/) are supported. Want to get up and running quickly with OAuth? Use Cortex's [Ruby client](https://github.com/cortex-cms/cortex-client-ruby) or [OmniAuth strategy](https://github.com/cb-talent-development/omniauth-cortex) for Client Credentials and Authorization Code grants, respectively. -Review the `optional_scopes` in Cortex's [Doorkeeper config](https://github.com/cbdr/cortex/blob/master/config/initializers/doorkeeper.rb) to determine the available scopes, and the [API Resource classes](https://github.com/cbdr/cortex/tree/master/app/api/v1/resources) to determine where they're required. - Before an application can consume any data, OAuth credentials must be created for the consuming application in the 'Applications' section of the Cortex admin interface. ### Content @@ -236,6 +234,6 @@ Cortex utilizes the Apache 2.0 License. See [LICENSE](LICENSE.md) for details. ## Copyright -Copyright (c) 2017 CareerBuilder, LLC. +Copyright (c) 2018 CareerBuilder, LLC. [cb-ce-github]: https://github.com/cb-talent-development "Content Enablement on GitHub" diff --git a/Rakefile b/Rakefile index dc6ff36a3..c14471f11 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,22 @@ -# Add your own tasks in files placed in lib/tasks ending in .rake, -# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. +begin + require 'bundler/setup' +rescue LoadError + puts 'You must `gem install bundler` and `bundle install` to run rake tasks' +end -Bundler.require(:tasks) -require_relative 'config/application' +require 'rdoc/task' -Cortex::Application.load_tasks +RDoc::Task.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'Cortex' + rdoc.options << '--line-numbers' + rdoc.rdoc_files.include('README.md') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__) +load 'rails/tasks/engine.rake' + +load 'rails/tasks/statistics.rake' + +require 'bundler/gem_tasks' diff --git a/app/assets/config/cortex_manifest.js b/app/assets/config/cortex_manifest.js new file mode 100644 index 000000000..a0f7d1b2f --- /dev/null +++ b/app/assets/config/cortex_manifest.js @@ -0,0 +1,4 @@ +//= link_tree ../images/cortex +//= link_tree ../fonts/cortex +//= link cortex/authentication.js +//= link cortex/authentication.scss diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js deleted file mode 100644 index c372c474c..000000000 --- a/app/assets/config/manifest.js +++ /dev/null @@ -1,4 +0,0 @@ -//= link_tree ../images -//= link_tree ../fonts -//= link_directory ../javascripts .js -//= link_directory ../stylesheets .css diff --git a/app/assets/fonts/Montserrat-Light.otf b/app/assets/fonts/cortex/Montserrat-Light.otf similarity index 100% rename from app/assets/fonts/Montserrat-Light.otf rename to app/assets/fonts/cortex/Montserrat-Light.otf diff --git a/app/assets/fonts/Montserrat-Medium.otf b/app/assets/fonts/cortex/Montserrat-Medium.otf similarity index 100% rename from app/assets/fonts/Montserrat-Medium.otf rename to app/assets/fonts/cortex/Montserrat-Medium.otf diff --git a/app/assets/fonts/Montserrat-Regular.otf b/app/assets/fonts/cortex/Montserrat-Regular.otf similarity index 100% rename from app/assets/fonts/Montserrat-Regular.otf rename to app/assets/fonts/cortex/Montserrat-Regular.otf diff --git a/app/assets/fonts/Montserrat-SemiBold.otf b/app/assets/fonts/cortex/Montserrat-SemiBold.otf similarity index 100% rename from app/assets/fonts/Montserrat-SemiBold.otf rename to app/assets/fonts/cortex/Montserrat-SemiBold.otf diff --git a/app/assets/images/check.svg b/app/assets/images/check.svg deleted file mode 100644 index 84666e1f0..000000000 --- a/app/assets/images/check.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - diff --git a/app/assets/images/favicon.ico b/app/assets/images/cortex/favicon.ico similarity index 100% rename from app/assets/images/favicon.ico rename to app/assets/images/cortex/favicon.ico diff --git a/app/assets/images/cortex/icons/add.svg b/app/assets/images/cortex/icons/add.svg new file mode 100644 index 000000000..a051515cb --- /dev/null +++ b/app/assets/images/cortex/icons/add.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/app/assets/images/icons/blog.png b/app/assets/images/cortex/icons/blog.png similarity index 100% rename from app/assets/images/icons/blog.png rename to app/assets/images/cortex/icons/blog.png diff --git a/app/assets/images/icons/media.png b/app/assets/images/cortex/icons/media.png similarity index 100% rename from app/assets/images/icons/media.png rename to app/assets/images/cortex/icons/media.png diff --git a/app/assets/images/logo.svg b/app/assets/images/cortex/logo.svg similarity index 100% rename from app/assets/images/logo.svg rename to app/assets/images/cortex/logo.svg diff --git a/app/assets/images/icons/add.svg b/app/assets/images/icons/add.svg deleted file mode 100644 index 45a65bba4..000000000 --- a/app/assets/images/icons/add.svg +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js deleted file mode 100644 index 1417c494e..000000000 --- a/app/assets/javascripts/application.js +++ /dev/null @@ -1,22 +0,0 @@ -//= require jquery3 -// require jquery.turbolinks # temporarily disabled -//= require rails-ujs -//= require underscore/underscore.js -//= require material -//= require jquery-ui/widgets/datepicker -//= require jquery-ui-timepicker-addon/dist/jquery-ui-timepicker-addon -//= require bootstrap-tagsinput -//= require clipboard/dist/clipboard.js -//= require dialog-polyfill -//= require moment/moment.js - -//= require cortex-plugins-core/application - -//= require base -//= require datepicker_init -//= require form -//= require flash -//= require media_dialogs -//= require quick_links - -// require turbolinks # temporarily disabled diff --git a/app/assets/javascripts/authentication.js b/app/assets/javascripts/authentication.js deleted file mode 100644 index 0fcfcd11e..000000000 --- a/app/assets/javascripts/authentication.js +++ /dev/null @@ -1,9 +0,0 @@ -//= require jquery3 -// require jquery.turbolinks # temporarily disabled -//= require rails-ujs -//= require underscore/underscore.js -//= require material - -//= require base - -// require turbolinks # temporarily disabled diff --git a/app/assets/javascripts/cortex/application.js b/app/assets/javascripts/cortex/application.js new file mode 100644 index 000000000..f53b642f8 --- /dev/null +++ b/app/assets/javascripts/cortex/application.js @@ -0,0 +1,19 @@ +//= require jquery +//= require rails-ujs +//= require underscore/underscore.js +//= require material-design-lite +//= require jquery-ui-bundle/jquery-ui +//= require jquery-ui-timepicker-addon/dist/jquery-ui-timepicker-addon +//= require bootstrap-tagsinput +//= require clipboard/dist/clipboard.js +//= require dialog-polyfill +//= require moment/moment.js + +//= require cortex-plugins-core/application + +//= require cortex/base +//= require cortex/datepicker_init +//= require cortex/form +//= require cortex/flash +//= require cortex/media_dialogs +//= require cortex/quick_links diff --git a/app/assets/javascripts/cortex/authentication.js b/app/assets/javascripts/cortex/authentication.js new file mode 100644 index 000000000..634e26221 --- /dev/null +++ b/app/assets/javascripts/cortex/authentication.js @@ -0,0 +1,6 @@ +//= require jquery +//= require rails-ujs +//= require underscore/underscore.js +//= require material-design-lite + +//= require cortex/base diff --git a/app/assets/javascripts/base.js b/app/assets/javascripts/cortex/base.js similarity index 69% rename from app/assets/javascripts/base.js rename to app/assets/javascripts/cortex/base.js index 841bdeb4e..e6fdb17e7 100644 --- a/app/assets/javascripts/base.js +++ b/app/assets/javascripts/cortex/base.js @@ -1,6 +1,6 @@ -(function() { +(function () { // See: http://stackoverflow.com/questions/32923179/material-design-lite-not-working-with-turbolinks - $(document).on('turbolinks:load', function() { + $(document).on('turbolinks:load', function () { componentHandler.upgradeDom(); }); })(); diff --git a/app/assets/javascripts/datepicker_init.js b/app/assets/javascripts/cortex/datepicker_init.js similarity index 88% rename from app/assets/javascripts/datepicker_init.js rename to app/assets/javascripts/cortex/datepicker_init.js index 2f890d3fc..184516fef 100644 --- a/app/assets/javascripts/datepicker_init.js +++ b/app/assets/javascripts/cortex/datepicker_init.js @@ -1,4 +1,4 @@ -var datepicker_init = function() { +var datepicker_init = function () { var datepicker = $('.datepicker'); if (datepicker.length) { @@ -12,7 +12,7 @@ var datepicker_init = function() { } }); - datepicker.on('focusout', function(ev){ + datepicker.on('focusout', function (ev) { if ($(this).val() === "") { var button = $('.form-button--submission'); diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/cortex/flash.js similarity index 65% rename from app/assets/javascripts/flash.js rename to app/assets/javascripts/cortex/flash.js index 7b7e62c3e..8d56552dd 100644 --- a/app/assets/javascripts/flash.js +++ b/app/assets/javascripts/cortex/flash.js @@ -1,7 +1,7 @@ -setTimeout(function(){ +setTimeout(function () { $('.mdl-js-snackbar').toggleClass('mdl-snackbar--active'); }, 2500); -$('.mdl-snackbar__close').click(function(){ +$('.mdl-snackbar__close').click(function () { $('.mdl-snackbar').removeClass('mdl-snackbar--active'); }) diff --git a/app/assets/javascripts/form.js b/app/assets/javascripts/cortex/form.js similarity index 79% rename from app/assets/javascripts/form.js rename to app/assets/javascripts/cortex/form.js index a09f9a236..2c57eec17 100644 --- a/app/assets/javascripts/form.js +++ b/app/assets/javascripts/cortex/form.js @@ -1,4 +1,4 @@ -var requiredFormField = function() { +var requiredFormField = function () { $('input[data-required=true]').attr('required', true); } diff --git a/app/assets/javascripts/media_dialogs.js b/app/assets/javascripts/cortex/media_dialogs.js similarity index 65% rename from app/assets/javascripts/media_dialogs.js rename to app/assets/javascripts/cortex/media_dialogs.js index f1e2fc115..e11ad4fa2 100644 --- a/app/assets/javascripts/media_dialogs.js +++ b/app/assets/javascripts/cortex/media_dialogs.js @@ -4,31 +4,31 @@ var dialogs = document.querySelectorAll('dialog'); global.dialogs = {}; - [].forEach.call(dialogs, function(dialog) { + [].forEach.call(dialogs, function (dialog) { dialogPolyfill.registerDialog(dialog); global.dialogs[dialog.id] = dialog; }); - $('.close-dialog').on('click', function(event) { + $('.close-dialog').on('click', function (event) { closeDialog(event); }); - $('body').on('click', function(event) { + $('body').on('click', function (event) { return var dialog = document.getElementById(event.target.closest('dialog').id); var rect = dialog.getBoundingClientRect(); - var isInDialog=(rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width); + var isInDialog = (rect.top <= event.clientY && event.clientY <= rect.top + rect.height && rect.left <= event.clientX && event.clientX <= rect.left + rect.width); if (!isInDialog) { closeDialog(event); } }); - global.blur_backdrop = function() { + global.blur_backdrop = function () { $('.mdl-layout__container').addClass('blur'); }; - global.unblur_backdrop = function() { + global.unblur_backdrop = function () { $('.mdl-layout__container').removeClass('blur'); }; diff --git a/app/assets/javascripts/quick_links.js b/app/assets/javascripts/cortex/quick_links.js similarity index 92% rename from app/assets/javascripts/quick_links.js rename to app/assets/javascripts/cortex/quick_links.js index ff29b9efb..751c38833 100644 --- a/app/assets/javascripts/quick_links.js +++ b/app/assets/javascripts/cortex/quick_links.js @@ -1,7 +1,7 @@ (function (global) { 'use strict'; - $(document).ready(function() { + $(document).ready(function () { $('.quick-links .jumbo-button').hover(function () { $(this).find('.jumbo-button__content__icon').css('display', 'none'); $(this).find('.jumbo-button__content__add').css('display', 'block'); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss deleted file mode 100644 index 68341b7cb..000000000 --- a/app/assets/stylesheets/application.scss +++ /dev/null @@ -1,37 +0,0 @@ -@import 'variables/material_overrides'; - -@import 'material'; -@import 'bourbon'; -@import 'bootstrap-namespaced'; -@import 'jquery-ui/datepicker'; -@import "jquery-ui-timepicker-addon/dist/jquery-ui-timepicker-addon"; -@import 'bootstrap-tagsinput.scss'; -@import 'dialog-polyfill'; - -@import 'directives/aspect-ratio'; - -@import 'variables/colors'; -@import 'variables/typography'; - -@import 'base'; -@import 'helpers'; -@import 'layout'; - -@import 'components/card'; -@import 'components/confetti'; -@import 'components/dialog'; -@import 'components/flash'; -@import 'components/form'; -@import 'components/header'; -@import 'components/index'; -@import 'components/jumbo-button'; -@import 'components/notes'; -@import 'components/sidebar'; -@import 'components/tenant_switcher'; -@import 'components/table'; -@import 'components/ui'; -@import 'components/loaders'; - -@import 'demo'; - -@import 'cortex-plugins-core/application'; diff --git a/app/assets/stylesheets/components/confetti.scss b/app/assets/stylesheets/components/confetti.scss deleted file mode 100644 index 00c7af4d0..000000000 --- a/app/assets/stylesheets/components/confetti.scss +++ /dev/null @@ -1,40 +0,0 @@ -@include keyframes(bang) { - from { - @include transform( translate3d(0,0,0) ); - opacity: 1; - } -} - -.confetti { - width: 100%; - position: absolute; - - i { - position: absolute; - display: block; - width: 5px; - height: 7px; - opacity: 0; - } - - @for $i from 1 through 50 { - i:nth-of-type(#{$i}) { - @include transform( translate3d(random(190) - 100 + px,random(50) - 100 + px,0) rotate(random(360) + deg) ); - background: hsla(random(360),100%,50%,1); - @include animation(bang 2500ms ease-out forwards); - opacity: 0; - } - } -} - -.confetti--left i { - left: 0; -} - -.confetti--center i { - left: 50%; -} - -.confetti--right i { - right: 0; -} diff --git a/app/assets/stylesheets/cortex/application.scss b/app/assets/stylesheets/cortex/application.scss new file mode 100644 index 000000000..9b6c3b1ae --- /dev/null +++ b/app/assets/stylesheets/cortex/application.scss @@ -0,0 +1,34 @@ +@import 'cortex/variables/material-overrides'; + +@import 'material-design-lite/src/material-design-lite'; +@import 'material-icons'; +@import 'bourbon/app/assets/stylesheets/bourbon'; +@import 'bootstrap-namespaced'; +@import 'jquery-ui-bundle/jquery-ui'; +@import "jquery-ui-timepicker-addon/dist/jquery-ui-timepicker-addon"; +@import 'bootstrap-tagsinput/src/bootstrap-tagsinput'; +@import 'dialog-polyfill/dialog-polyfill'; + +@import 'cortex/directives/aspect-ratio'; + +@import 'cortex/variables/colors'; +@import 'cortex/variables/typography'; + +@import 'cortex/base'; +@import 'cortex/helpers'; +@import 'cortex/layout'; + +@import 'cortex/components/card'; +@import 'cortex/components/dialog'; +@import 'cortex/components/flash'; +@import 'cortex/components/form'; +@import 'cortex/components/header'; +@import 'cortex/components/index'; +@import 'cortex/components/jumbo-button'; +@import 'cortex/components/notes'; +@import 'cortex/components/sidebar'; +@import 'cortex/components/tenant-switcher'; +@import 'cortex/components/table'; +@import 'cortex/components/ui'; +@import 'cortex/components/loaders'; +@import 'cortex/demo'; diff --git a/app/assets/stylesheets/authentication.scss b/app/assets/stylesheets/cortex/authentication.scss similarity index 100% rename from app/assets/stylesheets/authentication.scss rename to app/assets/stylesheets/cortex/authentication.scss diff --git a/app/assets/stylesheets/base.scss b/app/assets/stylesheets/cortex/base.scss similarity index 100% rename from app/assets/stylesheets/base.scss rename to app/assets/stylesheets/cortex/base.scss diff --git a/app/assets/stylesheets/bootstrap-namespaced.scss b/app/assets/stylesheets/cortex/bootstrap-namespaced.scss similarity index 99% rename from app/assets/stylesheets/bootstrap-namespaced.scss rename to app/assets/stylesheets/cortex/bootstrap-namespaced.scss index ccf2d3fc7..f41c16e79 100644 --- a/app/assets/stylesheets/bootstrap-namespaced.scss +++ b/app/assets/stylesheets/cortex/bootstrap-namespaced.scss @@ -3,7 +3,6 @@ * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ - // Core variables and mixins @import "bootstrap-sass/assets/stylesheets/bootstrap/variables"; @import "bootstrap-sass/assets/stylesheets/bootstrap/mixins"; diff --git a/app/assets/stylesheets/components/card.scss b/app/assets/stylesheets/cortex/components/card.scss similarity index 100% rename from app/assets/stylesheets/components/card.scss rename to app/assets/stylesheets/cortex/components/card.scss diff --git a/app/assets/stylesheets/components/dialog.scss b/app/assets/stylesheets/cortex/components/dialog.scss similarity index 100% rename from app/assets/stylesheets/components/dialog.scss rename to app/assets/stylesheets/cortex/components/dialog.scss diff --git a/app/assets/stylesheets/components/flash.scss b/app/assets/stylesheets/cortex/components/flash.scss similarity index 100% rename from app/assets/stylesheets/components/flash.scss rename to app/assets/stylesheets/cortex/components/flash.scss diff --git a/app/assets/stylesheets/components/form.scss b/app/assets/stylesheets/cortex/components/form.scss similarity index 100% rename from app/assets/stylesheets/components/form.scss rename to app/assets/stylesheets/cortex/components/form.scss diff --git a/app/assets/stylesheets/components/header.scss b/app/assets/stylesheets/cortex/components/header.scss similarity index 100% rename from app/assets/stylesheets/components/header.scss rename to app/assets/stylesheets/cortex/components/header.scss diff --git a/app/assets/stylesheets/components/index.scss b/app/assets/stylesheets/cortex/components/index.scss similarity index 100% rename from app/assets/stylesheets/components/index.scss rename to app/assets/stylesheets/cortex/components/index.scss diff --git a/app/assets/stylesheets/components/jumbo-button.scss b/app/assets/stylesheets/cortex/components/jumbo-button.scss similarity index 100% rename from app/assets/stylesheets/components/jumbo-button.scss rename to app/assets/stylesheets/cortex/components/jumbo-button.scss diff --git a/app/assets/stylesheets/components/loaders.scss b/app/assets/stylesheets/cortex/components/loaders.scss similarity index 100% rename from app/assets/stylesheets/components/loaders.scss rename to app/assets/stylesheets/cortex/components/loaders.scss diff --git a/app/assets/stylesheets/components/notes.scss b/app/assets/stylesheets/cortex/components/notes.scss similarity index 100% rename from app/assets/stylesheets/components/notes.scss rename to app/assets/stylesheets/cortex/components/notes.scss diff --git a/app/assets/stylesheets/components/sidebar.scss b/app/assets/stylesheets/cortex/components/sidebar.scss similarity index 100% rename from app/assets/stylesheets/components/sidebar.scss rename to app/assets/stylesheets/cortex/components/sidebar.scss diff --git a/app/assets/stylesheets/components/table.scss b/app/assets/stylesheets/cortex/components/table.scss similarity index 100% rename from app/assets/stylesheets/components/table.scss rename to app/assets/stylesheets/cortex/components/table.scss diff --git a/app/assets/stylesheets/components/tenant_switcher.scss b/app/assets/stylesheets/cortex/components/tenant-switcher.scss similarity index 100% rename from app/assets/stylesheets/components/tenant_switcher.scss rename to app/assets/stylesheets/cortex/components/tenant-switcher.scss diff --git a/app/assets/stylesheets/components/ui.scss b/app/assets/stylesheets/cortex/components/ui.scss similarity index 100% rename from app/assets/stylesheets/components/ui.scss rename to app/assets/stylesheets/cortex/components/ui.scss diff --git a/app/assets/stylesheets/demo.scss b/app/assets/stylesheets/cortex/demo.scss similarity index 100% rename from app/assets/stylesheets/demo.scss rename to app/assets/stylesheets/cortex/demo.scss diff --git a/app/assets/stylesheets/directives/aspect-ratio.scss b/app/assets/stylesheets/cortex/directives/aspect-ratio.scss similarity index 100% rename from app/assets/stylesheets/directives/aspect-ratio.scss rename to app/assets/stylesheets/cortex/directives/aspect-ratio.scss diff --git a/app/assets/stylesheets/cortex/helpers.scss b/app/assets/stylesheets/cortex/helpers.scss new file mode 100644 index 000000000..4c283cc1c --- /dev/null +++ b/app/assets/stylesheets/cortex/helpers.scss @@ -0,0 +1,1701 @@ +/* padding classes */ +.p-5 { + padding: 5px; +} + +.p-10 { + padding: 10px; +} + +.p-15 { + padding: 15px; +} + +.p-20 { + padding: 20px; +} + +.p-25 { + padding: 25px; +} + +.p-30 { + padding: 30px; +} + +.p-35 { + padding: 35px; +} + +.p-40 { + padding: 40px; +} + +.p-45 { + padding: 45px; +} + +.p-50 { + padding: 50px; +} + +.p-55 { + padding: 55px; +} + +.p-60 { + padding: 60px; +} + +.p-65 { + padding: 65px; +} + +.p-70 { + padding: 70px; +} + +.p-75 { + padding: 75px; +} + +.p-80 { + padding: 80px; +} + +/* padding 5 */ +.p-t-5 { + padding-top: 5px; +} + +.p-r-5 { + padding-right: 5px; +} + +.p-b-5 { + padding-bottom: 5px; +} + +.p-l-5 { + padding-left: 5px; +} + +.p-v-5 { + padding: 5px 0; +} + +.p-h-5 { + padding: 0 5px; +} + +/* padding 10 */ +.p-t-10 { + padding-top: 10px; +} + +.p-r-10 { + padding-right: 10px; +} + +.p-b-10 { + padding-bottom: 10px; +} + +.p-l-10 { + padding-left: 10px; +} + +.p-v-10 { + padding: 10px 0; +} + +.p-h-10 { + padding: 0 10px; +} + +/* padding 15 */ +.p-t-15 { + padding-top: 15px; +} + +.p-r-15 { + padding-right: 15px; +} + +.p-b-15 { + padding-bottom: 15px; +} + +.p-l-15 { + padding-left: 15px; +} + +.p-v-15 { + padding: 15px 0; +} + +.p-h-15 { + padding: 0 15px; +} + +/* padding 20 */ +.p-t-20 { + padding-top: 20px; +} + +.p-r-20 { + padding-right: 20px; +} + +.p-b-20 { + padding-bottom: 20px; +} + +.p-l-20 { + padding-left: 20px; +} + +.p-v-20 { + padding: 20px 0; +} + +.p-h-20 { + padding: 0 20px; +} + +/* padding 25 */ +.p-t-25 { + padding-top: 25px; +} + +.p-r-25 { + padding-right: 25px; +} + +.p-b-25 { + padding-bottom: 25px; +} + +.p-l-25 { + padding-left: 25px; +} + +.p-v-25 { + padding: 25px 0; +} + +.p-h-25 { + padding: 0 25px; +} + +/* padding 30 */ +.p-t-30 { + padding-top: 30px; +} + +.p-r-30 { + padding-right: 30px; +} + +.p-b-30 { + padding-bottom: 30px; +} + +.p-l-30 { + padding-left: 30px; +} + +.p-v-30 { + padding: 30px 0; +} + +.p-h-30 { + padding: 0 30px; +} + +/* padding 35 */ +.p-t-35 { + padding-top: 35px; +} + +.p-r-35 { + padding-right: 35px; +} + +.p-b-35 { + padding-bottom: 35px; +} + +.p-l-35 { + padding-left: 35px; +} + +.p-v-35 { + padding: 35px 0; +} + +.p-h-35 { + padding: 0 35px; +} + +/* padding 40 */ +.p-t-40 { + padding-top: 40px; +} + +.p-r-40 { + padding-right: 40px; +} + +.p-b-40 { + padding-bottom: 40px; +} + +.p-l-40 { + padding-left: 40px; +} + +.p-v-40 { + padding-top: 40px; + padding-bottom: 40px; +} + +.p-h-40 { + padding: 0 40px; +} + +/* padding 45 */ +.p-t-45 { + padding-top: 45px; +} + +.p-r-45 { + padding-right: 45px; +} + +.p-b-45 { + padding-bottom: 45px; +} + +.p-l-45 { + padding-left: 45px; +} + +.p-v-45 { + padding: 45px 0; +} + +.p-h-45 { + padding: 0 45px; +} + +/* padding 50 */ +.p-t-50 { + padding-top: 50px; +} + +.p-r-50 { + padding-right: 50px; +} + +.p-b-50 { + padding-bottom: 50px; +} + +.p-l-50 { + padding-left: 50px; +} + +.p-v-50 { + padding: 50px 0; +} + +.p-h-50 { + padding: 0 50px; +} + +/* padding 55 */ +.p-t-55 { + padding-top: 55px; +} + +.p-r-55 { + padding-right: 55px; +} + +.p-b-55 { + padding-bottom: 55px; +} + +.p-l-55 { + padding-left: 55px; +} + +.p-v-55 { + padding: 55px 0; +} + +.p-h-55 { + padding: 0 55px; +} + +/* padding 60 */ +.p-t-60 { + padding-top: 60px; +} + +.p-r-60 { + padding-right: 60px; +} + +.p-b-60 { + padding-bottom: 60px; +} + +.p-l-60 { + padding-left: 60px; +} + +.p-v-60 { + padding: 60px 0; +} + +.p-h-60 { + padding: 0 60px; +} + +/* padding 65 */ +.p-t-65 { + padding-top: 65px; +} + +.p-r-65 { + padding-right: 65px; +} + +.p-b-65 { + padding-bottom: 65px; +} + +.p-l-65 { + padding-left: 65px; +} + +.p-v-65 { + padding: 65px 0; +} + +.p-h-65 { + padding: 0 65px; +} + +/* padding 70 */ +.p-t-70 { + padding-top: 70px; +} + +.p-r-70 { + padding-right: 70px; +} + +.p-b-70 { + padding-bottom: 70px; +} + +.p-l-70 { + padding-left: 70px; +} + +.p-v-70 { + padding: 70px 0; +} + +.p-h-70 { + padding: 0 70px; +} + +/* padding 75 */ +.p-t-75 { + padding-top: 75px; +} + +.p-r-75 { + padding-right: 75px; +} + +.p-b-75 { + padding-bottom: 75px; +} + +.p-l-75 { + padding-left: 75px; +} + +.p-v-75 { + padding: 75px 0; +} + +.p-h-75 { + padding: 0 75px; +} + +/* padding 80 */ +.p-t-80 { + padding-top: 80px; +} + +.p-r-80 { + padding-right: 80px; +} + +.p-b-80 { + padding-bottom: 80px; +} + +.p-l-80 { + padding-left: 80px; +} + +.p-v-80 { + padding: 80px 0; +} + +.p-h-80 { + padding: 0 80px; +} + +/* padding 0 */ +.no-p { + padding: 0px; +} + +.no-p-t { + padding-top: 0px; +} + +.no-p-r { + padding-right: 0px; +} + +.no-p-b { + padding-bottom: 0px; +} + +.no-p-l { + padding-left: 0px; +} + +.no-p-v { + padding: 0px 0; +} + +.no-p-h { + padding: 0 0px; +} + +/* margin classes */ + +/* margin 5 */ +.m-5 { + margin: 5px; +} + +.m-t-5 { + margin-top: 5px; +} + +.m-r-5 { + margin-right: 5px; +} + +.m-b-5 { + margin-bottom: 5px; +} + +.m-l-5 { + margin-left: 5px; +} + +.m-v-5 { + margin: 5px 0; +} + +.m-h-5 { + margin: 0 5px; +} + +/* margin 10 */ +.m-10 { + margin: 10px; +} + +.m-t-10 { + margin-top: 10px; +} + +.m-r-10 { + margin-right: 10px; +} + +.m-b-10 { + margin-bottom: 10px; +} + +.m-l-10 { + margin-left: 10px; +} + +.m-v-10 { + margin: 10px 0; +} + +.m-h-10 { + margin: 0 10px; +} + +/* margin 15 */ +.m-15 { + margin: 15px; +} + +.m-t-15 { + margin-top: 15px; +} + +.m-r-15 { + margin-right: 15px; +} + +.m-b-15 { + margin-bottom: 15px; +} + +.m-l-15 { + margin-left: 15px; +} + +.m-v-15 { + margin: 15px 0; +} + +.m-h-15 { + margin: 0 15px; +} + +/* margin 20 */ +.m-20 { + margin: 20px; +} + +.m-t-20 { + margin-top: 20px; +} + +.m-r-20 { + margin-right: 20px; +} + +.m-b-20 { + margin-bottom: 20px; +} + +.m-l-20 { + margin-left: 20px; +} + +.m-v-20 { + margin: 20px 0; +} + +.m-h-20 { + margin: 0 20px; +} + +/* margin 25 */ +.m-25 { + margin: 25px; +} + +.m-t-25 { + margin-top: 25px; +} + +.m-r-25 { + margin-right: 25px; +} + +.m-b-25 { + margin-bottom: 25px; +} + +.m-l-25 { + margin-left: 25px; +} + +.m-v-25 { + margin: 25px 0; +} + +.m-h-25 { + margin: 0 25px; +} + +/* margin 30 */ +.m-30 { + margin: 30px; +} + +.m-t-30 { + margin-top: 30px; +} + +.m-r-30 { + margin-right: 30px; +} + +.m-b-30 { + margin-bottom: 30px; +} + +.m-l-30 { + margin-left: 30px; +} + +.m-v-30 { + margin: 30px 0; +} + +.m-h-30 { + margin: 0 30px; +} + +/* margin 35 */ +.m-35 { + margin: 35px; +} + +.m-t-35 { + margin-top: 35px; +} + +.m-r-35 { + margin-right: 35px; +} + +.m-b-35 { + margin-bottom: 35px; +} + +.m-l-35 { + margin-left: 35px; +} + +.m-v-35 { + margin: 35px 0; +} + +.m-h-35 { + margin: 0 35px; +} + +/* margin 40 */ +.m-40 { + margin: 40px; +} + +.m-t-40 { + margin-top: 40px; +} + +.m-r-40 { + margin-right: 40px; +} + +.m-b-40 { + margin-bottom: 40px; +} + +.m-l-40 { + margin-left: 40px; +} + +.m-v-40 { + margin: 40px 0; +} + +.m-h-40 { + margin: 0 40px; +} + +/* margin 45 */ +.m-45 { + margin: 45px; +} + +.m-t-45 { + margin-top: 45px; +} + +.m-r-45 { + margin-right: 45px; +} + +.m-b-45 { + margin-bottom: 45px; +} + +.m-l-45 { + margin-left: 45px; +} + +.m-v-45 { + margin: 45px 0; +} + +.m-h-45 { + margin: 0 45px; +} + +/* margin 50 */ +.m-50 { + margin: 50px; +} + +.m-t-50 { + margin-top: 50px; +} + +.m-r-50 { + margin-right: 50px; +} + +.m-b-50 { + margin-bottom: 50px; +} + +.m-l-50 { + margin-left: 50px; +} + +.m-v-50 { + margin: 50px 0; +} + +.m-h-50 { + margin: 0 50px; +} + +/* margin 55 */ +.m-55 { + margin: 55px; +} + +.m-t-55 { + margin-top: 55px; +} + +.m-r-55 { + margin-right: 55px; +} + +.m-b-55 { + margin-bottom: 55px; +} + +.m-l-55 { + margin-left: 55px; +} + +.m-v-55 { + margin: 55px 0; +} + +.m-h-55 { + margin: 0 55px; +} + +/* margin 60 */ +.m-60 { + margin: 60px; +} + +.m-t-60 { + margin-top: 60px; +} + +.m-r-60 { + margin-right: 60px; +} + +.m-b-60 { + margin-bottom: 60px; +} + +.m-l-60 { + margin-left: 60px; +} + +.m-v-60 { + margin: 60px 0; +} + +.m-h-60 { + margin: 0 60px; +} + +/* margin 65 */ +.m-65 { + margin: 65px; +} + +.m-t-65 { + margin-top: 65px; +} + +.m-r-65 { + margin-right: 65px; +} + +.m-b-65 { + margin-bottom: 65px; +} + +.m-l-65 { + margin-left: 65px; +} + +.m-v-65 { + margin: 65px 0; +} + +.m-h-65 { + margin: 0 65px; +} + +/* margin 70 */ +.m-70 { + margin: 70px; +} + +.m-t-70 { + margin-top: 70px; +} + +.m-r-70 { + margin-right: 70px; +} + +.m-b-70 { + margin-bottom: 70px; +} + +.m-l-70 { + margin-left: 70px; +} + +.m-v-70 { + margin: 70px 0; +} + +.m-h-70 { + margin: 0 70px; +} + +/* margin 75 */ +.m-75 { + margin: 75px; +} + +.m-t-75 { + margin-top: 75px; +} + +.m-r-75 { + margin-right: 75px; +} + +.m-b-75 { + margin-bottom: 75px; +} + +.m-l-75 { + margin-left: 75px; +} + +.m-v-75 { + margin: 75px 0; +} + +.m-h-75 { + margin: 0 75px; +} + +/* margin 80 */ +.m-80 { + margin: 80px; +} + +.m-t-80 { + margin-top: 80px; +} + +.m-r-80 { + margin-right: 80px; +} + +.m-b-80 { + margin-bottom: 80px; +} + +.m-l-80 { + margin-left: 80px; +} + +.m-v-80 { + margin: 80px 0; +} + +.m-h-80 { + margin: 0 80px; +} + +/* margin 0 */ +.no-m { + margin: 0px; +} + +.no-m-t { + margin-top: 0px; +} + +.no-m-r { + margin-right: 0px; +} + +.no-m-b { + margin-bottom: 0px; +} + +.no-m-l { + margin-left: 0px; +} + +.no-m-v { + margin: 0px 0; +} + +.no-m-h { + margin: 0 0px; +} + +/* static non-generated stuff */ +/* margin helpers */ +.no-margin { + margin: 0 !important; +} + +.no-m-lr, .no-m-rl { + margin-left: 0 !important; + margin-right: 0 !important; +} + +.no-m-tb, .no-m-bt { + margin-top: 0 !important; + margin-bottom: 0 !important; +} + +.m-t-only { + margin-left: 0 !important; + margin-right: 0 !important; + margin-bottom: 0 !important; +} + +.m-b-only { + margin-left: 0 !important; + margin-right: 0 !important; + margin-top: 0 !important; +} + +.m-l-only { + margin-right: 0 !important; + margin-top: 0 !important; + margin-bottom: 0 !important; +} + +.m-r-only { + margin-left: 0 !important; + margin-top: 0 !important; + margin-bottom: 0 !important; +} + +.m-auto { + margin-left: auto; + margin-right: auto; +} + +/* padding helpers */ +.no-padding { + padding: 0 !important; +} + +.no-pad-lr, .no-pad-rl { + padding-left: 0 !important; + padding-right: 0 !important; +} + +.no-pad-tb, .no-pad-bt { + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +.pad-t-only { + padding-left: 0 !important; + padding-right: 0 !important; + padding-bottom: 0 !important; +} + +.pad-b-only { + padding-left: 0 !important; + padding-right: 0 !important; + padding-top: 0 !important; +} + +.pad-l-only { + padding-right: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +.pad-r-only { + padding-left: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +/* border helpers */ +.no-border { + border: none !important; +} + +.no-border-t { + border-top: none !important; +} + +.no-border-r { + border-right: none !important; +} + +.no-border-b { + border-bottom: none !important; +} + +.no-border-l { + border-left: none !important; +} + +.no-border-lr, .no-border-rl { + border-left: none !important; + border-right: none !important; +} + +.no-border-tb, .no-border-bt { + border-top: none !important; + border-bottom: none !important; +} + +.no-border-tl, .no-border-lt { + border-top: none !important; + border-left: none !important; +} + +.no-border-tr, .no-border-rt { + border-top: none !important; + border-right: none !important; +} + +.no-border-bl, .no-border-lb { + border-bottom: none !important; + border-left: none !important; +} + +.no-border-br, .no-border-rb { + border-bottom: none !important; + border-right: none !important; +} + +.border-dashed { + border-style: dashed !important; +} + +.border-dotted { + border-style: dashed !important; +} + +.border-black { + border-color: black; +} + +.border-light-grey { + border-color: #e5e5e5; +} + +.border-medium-grey { + border-color: #cccccc; +} + +.border-grey { + border-color: #999999; +} + +.border-dark-grey { + border-color: #222222; +} + +.border-white, .border-fff { + border-color: white; +} + +/* background colors */ +.bg-black { + background-color: black; +} + +.bg-light-grey { + background-color: #e5e5e5; +} + +.bg-medium-grey { + background-color: #cccccc; +} + +.bg-grey { + background-color: #999999; +} + +.bg-dark-grey { + background-color: #222222; +} + +.bg-white, .bg-fff { + background-color: white; +} + +.bg-none, .no-bg { + background: none; + background-image: none; + background-color: transparent; +} + +.bg-facebook { + background-color: #47639E; +} + +.bg-twitter { + background-color: #02A8F3; +} + +/* font stuff */ +.lh-1 { + line-height: 1 !important; +} + +.lh-13 { + line-height: 1.3 !important; +} + +.lh-15 { + line-height: 1.5 !important; +} + +.bold, .strong { + font-weight: bold; +} + +.no-bold { + font-weight: normal; +} + +.italic, .em { + font-style: italic; +} + +.strike { + text-decoration: line-through; +} + +.underline { + text-decoration: underline; +} + +.normal { + font-weight: normal; + font-style: normal; +} + +.sans-serif { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.serif { + font-family: Georgia, "Times New Romain", serif; +} + +.uppercase { + text-transform: uppercase; +} + +.t-right { + text-align: right +} + +.t-center { + text-align: center +} + +.mw400 { + max-width: 400px; +} + +.mw500 { + max-width: 500px; +} + +.mw600 { + max-width: 600px; +} + +.w100 { + font-weight: 100; +} + +.w200 { + font-weight: 200; +} + +.w300 { + font-weight: 300; +} + +.w400 { + font-weight: 400; +} + +.w500 { + font-weight: 500; +} + +.w600 { + font-weight: 600; +} + +.w700 { + font-weight: 700; +} + +.w800 { + font-weight: 800; +} + +.w900 { + font-weight: 900; +} + +.f9 { + font-size: 9px; +} + +.f10 { + font-size: 10px; +} + +.f11 { + font-size: 11px; +} + +.f12 { + font-size: 12px; +} + +.f13 { + font-size: 13px; +} + +.f14 { + font-size: 14px; +} + +.f15 { + font-size: 15px; +} + +.f16 { + font-size: 16px; +} + +.f17 { + font-size: 17px; +} + +.f18 { + font-size: 18px; + line-height: 30px; +} + +.f19 { + font-size: 19px; +} + +.f20 { + font-size: 20px; +} + +.f30 { + font-size: 30px; +} + +.f40 { + font-size: 40px; +} + +.f50 { + font-size: 50px; +} + +.f60 { + font-size: 60px; +} + +.size-small { + font-size: 75% !important; +} + +.size-normal { + font-size: 100% !important; +} + +.size-medium { + font-size: 125% !important; +} + +.size-big, .size-large { + font-size: 150% !important; +} + +.size-huge { + font-size: 200% !important; +} + +.inherit { + font: inherit; +} + +.no-wrap { + white-space: nowrap; +} + +.auto-cell-size { + white-space: nowrap; + width: 1%; +} + +.ls-0 { + letter-spacing: -3px; + margin-left: 10px; + margin-right: 10px; + white-space: nowrap; +} + +.no-ul, .no-ul:hover, .no-ul a, .no-ul a:hover { + text-decoration: none; +} + +/* can go on or on its parent */ + +/* general helpers */ +.color-inherit { + color: inherit; +} + +.clear { + clear: both; +} + +.clear:after { + display: table; + content: " "; + clear: both; +} + +.f-left { + float: left; +} + +.f-right { + float: right; +} + +.f-none { + float: none; +} + +.block { + display: block !important; +} + +.inline { + display: inline !important; +} + +.table { + display: table; +} + +.in-block { + display: inline-block !important; + *display: inline !important; + zoom: 1; +} + +.d-none, .hide, .hidden { + display: none !important; +} + +.rel, .relative { + position: relative !important; +} + +.abs, .absolute { + position: absolute; +} + +.static { + position: static; +} + +.fixed { + position: fixed; +} + +.t-0 { + top: 0; +} + +.b-0 { + bottom: 0; +} + +.l-0 { + left: 0; +} + +.r-0 { + right: 0; +} + +.tb-0 { + top: 0; + bottom: 0; +} + +.lr-0 { + left: 0; + right: 0; +} + +.v-mid, .v-m { + vertical-align: middle !important; +} + +.v-top, .v-t { + vertical-align: top !important; +} + +.v-bottom, .v-b { + vertical-align: bottom !important; +} + +.v-super { + vertical-align: super !important; +} + +.full-input input, .full-input select, .full-input textarea { + width: 100%; +} + +.normal input, .normal select, .normal textarea, .full-input input[type=checkbox], .full-input input[type=radio] { + width: auto; +} + +.no-shadow { + box-shadow: none !important; +} + +.no-border-radius { + border-radius: 0 !important; +} + +.overflow-x-scroll { + max-width: 100%; + overflow-x: auto; +} + +.overflow-no { + overflow: hidden; +} + +.overflow-auto { + overflow: auto; +} + +.overflow-scroll { + overflow: scroll; +} + +.pointer { + cursor: pointer; +} + +.fullwidth { + width: 100%; +} + +.fullheight { + height: 100%; +} + +.nolist { + list-style: none; +} + +.text-shadow { + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.4); +} + +.radius3 { + border-radius: 3px; +} + +.radius5 { + border-radius: 5px; +} + +.radius10 { + border-radius: 10px; +} + +.text-rgb-2 { + color: rgba(0, 0, 0, 0.2); +} + +.text-rgb-3 { + color: rgba(0, 0, 0, 0.3); +} + +.text-rgb-4 { + color: rgba(0, 0, 0, 0.4); +} + +.text-rgb-5 { + color: rgba(0, 0, 0, 0.5); +} + +.mh-100 { + min-height: 100px; +} + +.mh-200 { + min-height: 200px; +} + +.mh-300 { + min-height: 300px; +} + +.margin-auto { + margin: 0% auto; +} + +.img-responsive { + display: block; + max-width: 100%; + height: auto; +} + +.flex { + display: -webkit-flex; + display: -moz-flex; + display: -ms-flexbox; + display: -o-flex; + display: flex; + +} + +.flex1 { + -webkit-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + -o-flex: 1; + flex: 1; + + height: 100%; + width: 100%; +} + +.flexrow { + -webkit-flex-direction: row; + -moz-flex-direction: row; + -ms-flex-direction: row; + -o-flex-direction: row; + flex-direction: row; +} + +.flex-right { + -webkit-flex-direction: row-reverse; + -moz-flex-direction: row-reverse; + -ms-flex-direction: row-reverse; + -o-flex-direction: row-reverse; + flex-direction: row-reverse; +} + +textarea { + resize: both; +} + +textarea.vertical { + resize: vertical; +} + +textarea.horizontal { + resize: vertical; +} + +textarea.noresize { + resize: none; +} + +@media (max-width: 479px) { + .hide-mobile { + display: none; + } +} + +@media (min-width: 480px) and (max-width: 839px) { + .hide-tablet { + display: none; + } + .p-r-10-tablet { + padding-right: 10px; + } + .p-l-10-tablet { + padding-left: 10px; + } +} + +@media (max-width: 839px) { + .hide-from-tablet { + display: none; + } + .p-20--small { + padding: 20px; + } +} diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/cortex/layout.scss similarity index 100% rename from app/assets/stylesheets/layout.scss rename to app/assets/stylesheets/cortex/layout.scss diff --git a/app/assets/stylesheets/cortex/material-icons.scss b/app/assets/stylesheets/cortex/material-icons.scss new file mode 100644 index 000000000..0e877efba --- /dev/null +++ b/app/assets/stylesheets/cortex/material-icons.scss @@ -0,0 +1,13 @@ +/* Rules for sizing the icon. */ +.material-icons.md-18 { font-size: 18px; } +.material-icons.md-24 { font-size: 24px; } +.material-icons.md-36 { font-size: 36px; } +.material-icons.md-48 { font-size: 48px; } + +/* Rules for using icons as black on a light background. */ +.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); } +.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); } + +/* Rules for using icons as white on a dark background. */ +.material-icons.md-light { color: rgba(255, 255, 255, 1); } +.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); } diff --git a/app/assets/stylesheets/variables/_colors.scss b/app/assets/stylesheets/cortex/variables/_colors.scss similarity index 99% rename from app/assets/stylesheets/variables/_colors.scss rename to app/assets/stylesheets/cortex/variables/_colors.scss index f83e5d0b1..52bb7ad4e 100644 --- a/app/assets/stylesheets/variables/_colors.scss +++ b/app/assets/stylesheets/cortex/variables/_colors.scss @@ -26,7 +26,6 @@ $color-red: #d85252; $color-white: white; // Header $color-black: #000000; - // Color Semantics $employer-color: $color-teal; diff --git a/app/assets/stylesheets/variables/_material_overrides.scss b/app/assets/stylesheets/cortex/variables/_material-overrides.scss similarity index 100% rename from app/assets/stylesheets/variables/_material_overrides.scss rename to app/assets/stylesheets/cortex/variables/_material-overrides.scss diff --git a/app/assets/stylesheets/variables/_typography.scss b/app/assets/stylesheets/cortex/variables/_typography.scss similarity index 86% rename from app/assets/stylesheets/variables/_typography.scss rename to app/assets/stylesheets/cortex/variables/_typography.scss index 516ad9f25..1bd03a46e 100644 --- a/app/assets/stylesheets/variables/_typography.scss +++ b/app/assets/stylesheets/cortex/variables/_typography.scss @@ -2,28 +2,28 @@ font-family: Montserrat; font-style: normal; font-weight: normal; - src: url(asset_path('Montserrat-Regular.otf')) format("opentype"); + src: url(asset_path('cortex/Montserrat-Regular.otf')) format("opentype"); } @font-face { font-family: Montserrat; font-style: normal; font-weight: bold; - src: url(asset_path('Montserrat-Medium.otf')) format("opentype"); + src: url(asset_path('cortex/Montserrat-Medium.otf')) format("opentype"); } @font-face { font-family: Montserrat; font-style: normal; font-weight: bolder; - src: url(asset_path('Montserrat-SemiBold.otf')) format("opentype"); + src: url(asset_path('cortex/Montserrat-SemiBold.otf')) format("opentype"); } @font-face { font-family: Montserrat; font-style: normal; font-weight: lighter; - src: url(asset_path('Montserrat-Light.otf')) format("opentype"); + src: url(asset_path('cortex/Montserrat-Light.otf')) format("opentype"); } $cortex-font-stack: Montserrat, sans-serif; diff --git a/app/assets/stylesheets/helpers.scss b/app/assets/stylesheets/helpers.scss deleted file mode 100644 index ccaf0a382..000000000 --- a/app/assets/stylesheets/helpers.scss +++ /dev/null @@ -1,559 +0,0 @@ -/* padding classes */ -.p-5 { padding: 5px; } -.p-10 { padding: 10px; } -.p-15 { padding: 15px; } -.p-20 { padding: 20px; } -.p-25 { padding: 25px; } -.p-30 { padding: 30px; } -.p-35 { padding: 35px; } -.p-40 { padding: 40px; } -.p-45 { padding: 45px; } -.p-50 { padding: 50px; } -.p-55 { padding: 55px; } -.p-60 { padding: 60px; } -.p-65 { padding: 65px; } -.p-70 { padding: 70px; } -.p-75 { padding: 75px; } -.p-80 { padding: 80px; } - -/* padding 5 */ -.p-t-5 { padding-top: 5px; } -.p-r-5 { padding-right: 5px; } -.p-b-5 { padding-bottom: 5px; } -.p-l-5 { padding-left: 5px; } -.p-v-5 { padding: 5px 0; } -.p-h-5 { padding: 0 5px; } - -/* padding 10 */ -.p-t-10 { padding-top: 10px; } -.p-r-10 { padding-right: 10px; } -.p-b-10 { padding-bottom: 10px; } -.p-l-10 { padding-left: 10px; } -.p-v-10 { padding: 10px 0; } -.p-h-10 { padding: 0 10px; } - -/* padding 15 */ -.p-t-15 { padding-top: 15px; } -.p-r-15 { padding-right: 15px; } -.p-b-15 { padding-bottom: 15px; } -.p-l-15 { padding-left: 15px; } -.p-v-15 { padding: 15px 0; } -.p-h-15 { padding: 0 15px; } - -/* padding 20 */ -.p-t-20 { padding-top: 20px; } -.p-r-20 { padding-right: 20px; } -.p-b-20 { padding-bottom: 20px; } -.p-l-20 { padding-left: 20px; } -.p-v-20 { padding: 20px 0; } -.p-h-20 { padding: 0 20px; } - -/* padding 25 */ -.p-t-25 { padding-top: 25px; } -.p-r-25 { padding-right: 25px; } -.p-b-25 { padding-bottom: 25px; } -.p-l-25 { padding-left: 25px; } -.p-v-25 { padding: 25px 0; } -.p-h-25 { padding: 0 25px; } - -/* padding 30 */ -.p-t-30 { padding-top: 30px; } -.p-r-30 { padding-right: 30px; } -.p-b-30 { padding-bottom: 30px; } -.p-l-30 { padding-left: 30px; } -.p-v-30 { padding: 30px 0; } -.p-h-30 { padding: 0 30px; } - -/* padding 35 */ -.p-t-35 { padding-top: 35px; } -.p-r-35 { padding-right: 35px; } -.p-b-35 { padding-bottom: 35px; } -.p-l-35 { padding-left: 35px; } -.p-v-35 { padding: 35px 0; } -.p-h-35 { padding: 0 35px; } - -/* padding 40 */ -.p-t-40 { padding-top: 40px; } -.p-r-40 { padding-right: 40px; } -.p-b-40 { padding-bottom: 40px; } -.p-l-40 { padding-left: 40px; } -.p-v-40 { padding-top: 40px; padding-bottom:40px; } -.p-h-40 { padding: 0 40px; } - -/* padding 45 */ -.p-t-45 { padding-top: 45px; } -.p-r-45 { padding-right: 45px; } -.p-b-45 { padding-bottom: 45px; } -.p-l-45 { padding-left: 45px; } -.p-v-45 { padding: 45px 0; } -.p-h-45 { padding: 0 45px; } - -/* padding 50 */ -.p-t-50 { padding-top: 50px; } -.p-r-50 { padding-right: 50px; } -.p-b-50 { padding-bottom: 50px; } -.p-l-50 { padding-left: 50px; } -.p-v-50 { padding: 50px 0; } -.p-h-50 { padding: 0 50px; } - -/* padding 55 */ -.p-t-55 { padding-top: 55px; } -.p-r-55 { padding-right: 55px; } -.p-b-55 { padding-bottom: 55px; } -.p-l-55 { padding-left: 55px; } -.p-v-55 { padding: 55px 0; } -.p-h-55 { padding: 0 55px; } - -/* padding 60 */ -.p-t-60 { padding-top: 60px; } -.p-r-60 { padding-right: 60px; } -.p-b-60 { padding-bottom: 60px; } -.p-l-60 { padding-left: 60px; } -.p-v-60 { padding: 60px 0; } -.p-h-60 { padding: 0 60px; } - -/* padding 65 */ -.p-t-65 { padding-top: 65px; } -.p-r-65 { padding-right: 65px; } -.p-b-65 { padding-bottom: 65px; } -.p-l-65 { padding-left: 65px; } -.p-v-65 { padding: 65px 0; } -.p-h-65 { padding: 0 65px; } - -/* padding 70 */ -.p-t-70 { padding-top: 70px; } -.p-r-70 { padding-right: 70px; } -.p-b-70 { padding-bottom: 70px; } -.p-l-70 { padding-left: 70px; } -.p-v-70 { padding: 70px 0; } -.p-h-70 { padding: 0 70px; } - -/* padding 75 */ -.p-t-75 { padding-top: 75px; } -.p-r-75 { padding-right: 75px; } -.p-b-75 { padding-bottom: 75px; } -.p-l-75 { padding-left: 75px; } -.p-v-75 { padding: 75px 0; } -.p-h-75 { padding: 0 75px; } - -/* padding 80 */ -.p-t-80 { padding-top: 80px; } -.p-r-80 { padding-right: 80px; } -.p-b-80 { padding-bottom: 80px; } -.p-l-80 { padding-left: 80px; } -.p-v-80 { padding: 80px 0; } -.p-h-80 { padding: 0 80px; } - -/* padding 0 */ -.no-p { padding: 0px; } -.no-p-t { padding-top: 0px; } -.no-p-r { padding-right: 0px; } -.no-p-b { padding-bottom: 0px; } -.no-p-l { padding-left: 0px; } -.no-p-v { padding: 0px 0; } -.no-p-h { padding: 0 0px; } - -/* margin classes */ - -/* margin 5 */ -.m-5 { margin: 5px; } -.m-t-5 { margin-top: 5px; } -.m-r-5 { margin-right: 5px; } -.m-b-5 { margin-bottom: 5px; } -.m-l-5 { margin-left: 5px; } -.m-v-5 { margin: 5px 0; } -.m-h-5 { margin: 0 5px; } - -/* margin 10 */ -.m-10 { margin: 10px; } -.m-t-10 { margin-top: 10px; } -.m-r-10 { margin-right: 10px; } -.m-b-10 { margin-bottom: 10px; } -.m-l-10 { margin-left: 10px; } -.m-v-10 { margin: 10px 0; } -.m-h-10 { margin: 0 10px; } - -/* margin 15 */ -.m-15 { margin: 15px; } -.m-t-15 { margin-top: 15px; } -.m-r-15 { margin-right: 15px; } -.m-b-15 { margin-bottom: 15px; } -.m-l-15 { margin-left: 15px; } -.m-v-15 { margin: 15px 0; } -.m-h-15 { margin: 0 15px; } - -/* margin 20 */ -.m-20 { margin: 20px; } -.m-t-20 { margin-top: 20px; } -.m-r-20 { margin-right: 20px; } -.m-b-20 { margin-bottom: 20px; } -.m-l-20 { margin-left: 20px; } -.m-v-20 { margin: 20px 0; } -.m-h-20 { margin: 0 20px; } - -/* margin 25 */ -.m-25 { margin: 25px; } -.m-t-25 { margin-top: 25px; } -.m-r-25 { margin-right: 25px; } -.m-b-25 { margin-bottom: 25px; } -.m-l-25 { margin-left: 25px; } -.m-v-25 { margin: 25px 0; } -.m-h-25 { margin: 0 25px; } - -/* margin 30 */ -.m-30 { margin: 30px; } -.m-t-30 { margin-top: 30px; } -.m-r-30 { margin-right: 30px; } -.m-b-30 { margin-bottom: 30px; } -.m-l-30 { margin-left: 30px; } -.m-v-30 { margin: 30px 0; } -.m-h-30 { margin: 0 30px; } - -/* margin 35 */ -.m-35 { margin: 35px; } -.m-t-35 { margin-top: 35px; } -.m-r-35 { margin-right: 35px; } -.m-b-35 { margin-bottom: 35px; } -.m-l-35 { margin-left: 35px; } -.m-v-35 { margin: 35px 0; } -.m-h-35 { margin: 0 35px; } - -/* margin 40 */ -.m-40 { margin: 40px; } -.m-t-40 { margin-top: 40px; } -.m-r-40 { margin-right: 40px; } -.m-b-40 { margin-bottom: 40px; } -.m-l-40 { margin-left: 40px; } -.m-v-40 { margin: 40px 0; } -.m-h-40 { margin: 0 40px; } - -/* margin 45 */ -.m-45 { margin: 45px; } -.m-t-45 { margin-top: 45px; } -.m-r-45 { margin-right: 45px; } -.m-b-45 { margin-bottom: 45px; } -.m-l-45 { margin-left: 45px; } -.m-v-45 { margin: 45px 0; } -.m-h-45 { margin: 0 45px; } - -/* margin 50 */ -.m-50 { margin: 50px; } -.m-t-50 { margin-top: 50px; } -.m-r-50 { margin-right: 50px; } -.m-b-50 { margin-bottom: 50px; } -.m-l-50 { margin-left: 50px; } -.m-v-50 { margin: 50px 0; } -.m-h-50 { margin: 0 50px; } - -/* margin 55 */ -.m-55 { margin: 55px; } -.m-t-55 { margin-top: 55px; } -.m-r-55 { margin-right: 55px; } -.m-b-55 { margin-bottom: 55px; } -.m-l-55 { margin-left: 55px; } -.m-v-55 { margin: 55px 0; } -.m-h-55 { margin: 0 55px; } - -/* margin 60 */ -.m-60 { margin: 60px; } -.m-t-60 { margin-top: 60px; } -.m-r-60 { margin-right: 60px; } -.m-b-60 { margin-bottom: 60px; } -.m-l-60 { margin-left: 60px; } -.m-v-60 { margin: 60px 0; } -.m-h-60 { margin: 0 60px; } - -/* margin 65 */ -.m-65 { margin: 65px; } -.m-t-65 { margin-top: 65px; } -.m-r-65 { margin-right: 65px; } -.m-b-65 { margin-bottom: 65px; } -.m-l-65 { margin-left: 65px; } -.m-v-65 { margin: 65px 0; } -.m-h-65 { margin: 0 65px; } - -/* margin 70 */ -.m-70 { margin: 70px; } -.m-t-70 { margin-top: 70px; } -.m-r-70 { margin-right: 70px; } -.m-b-70 { margin-bottom: 70px; } -.m-l-70 { margin-left: 70px; } -.m-v-70 { margin: 70px 0; } -.m-h-70 { margin: 0 70px; } - -/* margin 75 */ -.m-75 { margin: 75px; } -.m-t-75 { margin-top: 75px; } -.m-r-75 { margin-right: 75px; } -.m-b-75 { margin-bottom: 75px; } -.m-l-75 { margin-left: 75px; } -.m-v-75 { margin: 75px 0; } -.m-h-75 { margin: 0 75px; } - -/* margin 80 */ -.m-80 { margin: 80px; } -.m-t-80 { margin-top: 80px; } -.m-r-80 { margin-right: 80px; } -.m-b-80 { margin-bottom: 80px; } -.m-l-80 { margin-left: 80px; } -.m-v-80 { margin: 80px 0; } -.m-h-80 { margin: 0 80px; } - -/* margin 0 */ -.no-m { margin: 0px; } -.no-m-t { margin-top: 0px; } -.no-m-r { margin-right: 0px; } -.no-m-b { margin-bottom: 0px; } -.no-m-l { margin-left: 0px; } -.no-m-v { margin: 0px 0; } -.no-m-h { margin: 0 0px; } - - -/* static non-generated stuff */ -/* margin helpers */ -.no-margin { margin: 0 !important; } -.no-m-lr, .no-m-rl { margin-left: 0 !important; margin-right: 0 !important; } -.no-m-tb, .no-m-bt { margin-top: 0 !important; margin-bottom: 0 !important; } -.m-t-only { margin-left: 0 !important; margin-right: 0 !important; margin-bottom: 0 !important; } -.m-b-only { margin-left: 0 !important; margin-right: 0 !important; margin-top: 0 !important; } -.m-l-only { margin-right: 0 !important; margin-top: 0 !important; margin-bottom: 0 !important; } -.m-r-only { margin-left: 0 !important; margin-top: 0 !important; margin-bottom: 0 !important; } -.m-auto { margin-left: auto; margin-right: auto; } - -/* padding helpers */ -.no-padding { padding: 0 !important; } -.no-pad-lr, .no-pad-rl { padding-left: 0 !important; padding-right: 0 !important; } -.no-pad-tb, .no-pad-bt { padding-top: 0 !important; padding-bottom: 0 !important; } -.pad-t-only { padding-left: 0 !important; padding-right: 0 !important; padding-bottom: 0 !important; } -.pad-b-only { padding-left: 0 !important; padding-right: 0 !important; padding-top: 0 !important; } -.pad-l-only { padding-right: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; } -.pad-r-only { padding-left: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; } - -/* border helpers */ -.no-border { border: none !important; } -.no-border-t { border-top: none !important; } -.no-border-r { border-right: none !important; } -.no-border-b { border-bottom: none !important; } -.no-border-l { border-left: none !important; } -.no-border-lr, .no-border-rl { border-left: none !important; border-right: none !important; } -.no-border-tb, .no-border-bt { border-top: none !important; border-bottom: none !important; } -.no-border-tl, .no-border-lt { border-top: none !important; border-left: none !important; } -.no-border-tr, .no-border-rt { border-top: none !important; border-right: none !important; } -.no-border-bl, .no-border-lb { border-bottom: none !important; border-left: none !important; } -.no-border-br, .no-border-rb { border-bottom: none !important; border-right: none !important; } -.border-dashed { border-style: dashed !important; } -.border-dotted { border-style: dashed !important; } -.border-black { border-color: black; } -.border-light-grey { border-color: #e5e5e5; } -.border-medium-grey { border-color: #cccccc; } -.border-grey { border-color: #999999; } -.border-dark-grey { border-color: #222222; } -.border-white, .border-fff { border-color: white; } - -/* background colors */ -.bg-black { background-color: black; } -.bg-light-grey { background-color: #e5e5e5; } -.bg-medium-grey { background-color: #cccccc; } -.bg-grey { background-color: #999999; } -.bg-dark-grey { background-color: #222222; } -.bg-white, .bg-fff { background-color: white; } -.bg-none, .no-bg { background: none; background-image: none; background-color: transparent; } - -.bg-facebook { background-color: #47639E; } -.bg-twitter { background-color: #02A8F3; } - -/* font stuff */ -.lh-1 { line-height: 1 !important; } -.lh-13 { line-height: 1.3 !important; } -.lh-15 { line-height: 1.5 !important; } -.bold, .strong { font-weight: bold; } -.no-bold { font-weight: normal; } -.italic, .em { font-style: italic; } -.strike { text-decoration: line-through; } -.underline { text-decoration: underline; } -.normal { font-weight: normal; font-style: normal; } -.sans-serif { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } -.serif { font-family: Georgia, "Times New Romain", serif; } -.uppercase { text-transform: uppercase; } -.t-right { text-align: right } -.t-center { text-align: center } - - -.mw400 { max-width:400px; } -.mw500 { max-width:500px; } -.mw600 { max-width:600px; } - - -.w100 { font-weight: 100;} -.w200 { font-weight: 200;} -.w300 { font-weight: 300;} -.w400 { font-weight: 400;} -.w500 { font-weight: 500;} -.w600 { font-weight: 600;} -.w700 { font-weight: 700;} -.w800 { font-weight: 800;} -.w900 { font-weight: 900;} - - -.f9 { font-size:9px;} -.f10 { font-size:10px;} -.f11 { font-size:11px;} -.f12 { font-size:12px;} -.f13 { font-size:13px;} -.f14 { font-size:14px;} -.f15 { font-size:15px;} -.f16 { font-size:16px;} -.f17 { font-size:17px;} -.f18 { font-size:18px; line-height:30px; } -.f19 { font-size:19px;} -.f20 { font-size:20px;} -.f30 { font-size:30px;} -.f40 { font-size:40px;} -.f50 { font-size:50px;} -.f60 { font-size:60px;} - - -.size-small { font-size: 75% !important; } -.size-normal { font-size: 100% !important; } -.size-medium { font-size: 125% !important; } -.size-big, .size-large { font-size: 150% !important; } -.size-huge { font-size: 200% !important; } -.inherit { font: inherit; } -.no-wrap { white-space: nowrap; } -.auto-cell-size { white-space: nowrap; width:1%; } -.ls-0 { letter-spacing: -3px; margin-left: 10px; margin-right: 10px; white-space: nowrap; } -.no-ul, .no-ul:hover, .no-ul a, .no-ul a:hover { text-decoration: none; } -/* can go on or on its parent */ - -/* general helpers */ -.color-inherit { color: inherit; } -.clear { clear: both; } -.clear:after { display: table; content: " "; clear: both; } -.f-left { float: left; } -.f-right { float: right; } -.f-none { float: none; } -.block { display: block !important; } -.inline { display: inline !important; } -.table { display: table; } -.in-block { display: inline-block !important; *display: inline !important; zoom: 1; } -.d-none, .hide, .hidden { display: none !important; } -.rel, .relative { position: relative !important; } -.abs, .absolute { position: absolute; } -.static { position: static; } -.fixed { position: fixed; } -.t-0 { top: 0; } -.b-0 { bottom: 0; } -.l-0 { left: 0; } -.r-0 { right: 0; } -.tb-0 { top: 0; bottom: 0; } -.lr-0 { left: 0; right: 0; } -.v-mid, .v-m { vertical-align: middle !important; } -.v-top, .v-t { vertical-align: top !important; } -.v-bottom, .v-b { vertical-align: bottom !important; } -.v-super { vertical-align: super !important; } -.full-input input, .full-input select, .full-input textarea { width: 100%; } -.normal input, .normal select, .normal textarea, .full-input input[type=checkbox], .full-input input[type=radio] { width: auto; } -.no-shadow { box-shadow: none !important; } -.no-border-radius { border-radius: 0 !important; } -.overflow-x-scroll { max-width: 100%; overflow-x: auto; } -.overflow-no { overflow: hidden; } -.overflow-auto { overflow: auto; } -.overflow-scroll { overflow: scroll; } -.pointer { cursor: pointer; } - -.fullwidth { width: 100%; } -.fullheight { height: 100%; } -.nolist { list-style: none; } - -.text-shadow { text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.4); } -.radius3 {border-radius: 3px; } -.radius5 {border-radius: 5px; } -.radius10 {border-radius: 10px; } - -.text-rgb-2 { color: rgba(0, 0, 0, 0.2); } -.text-rgb-3 { color: rgba(0, 0, 0, 0.3); } -.text-rgb-4 { color: rgba(0, 0, 0, 0.4); } -.text-rgb-5 { color: rgba(0, 0, 0, 0.5); } - -.mh-100 { min-height: 100px; } -.mh-200 { min-height: 200px; } -.mh-300 { min-height: 300px; } - - -.margin-auto { - margin: 0% auto; -} -.img-responsive { - display: block; - max-width: 100%; - height: auto; -} - -.flex { - display: -webkit-flex; - display: -moz-flex; - display: -ms-flexbox; - display: -o-flex; - display: flex; - -} -.flex1 { - -webkit-flex: 1; - -moz-flex: 1; - -ms-flex: 1; - -o-flex: 1; - flex: 1; - - height: 100%; - width: 100%; -} -.flexrow { - -webkit-flex-direction: row; - -moz-flex-direction: row; - -ms-flex-direction: row; - -o-flex-direction: row; - flex-direction: row; -} -.flex-right { - -webkit-flex-direction: row-reverse; - -moz-flex-direction: row-reverse; - -ms-flex-direction: row-reverse; - -o-flex-direction: row-reverse; - flex-direction: row-reverse; -} - - -textarea { resize:both; } -textarea.vertical { resize:vertical; } -textarea.horizontal { resize:vertical; } -textarea.noresize { resize:none; } - - - - -@media (max-width: 479px) { - .hide-mobile { - display:none; - } -} - -@media (min-width: 480px) and (max-width: 839px) { - .hide-tablet { - display:none; - } - .p-r-10-tablet { - padding-right: 10px; - } - .p-l-10-tablet { - padding-left: 10px; - } -} - -@media (max-width: 839px) { - .hide-from-tablet { - display:none; - } - .p-20--small { - padding: 20px; - } -} diff --git a/app/cells/content_type_cell.rb b/app/cells/content_type_cell.rb deleted file mode 100644 index 828aa27ef..000000000 --- a/app/cells/content_type_cell.rb +++ /dev/null @@ -1,8 +0,0 @@ -class ContentTypeCell < Cell::ViewModel - property :name - property :icon - - def nav - render - end -end diff --git a/app/cells/cortex/application_cell.rb b/app/cells/cortex/application_cell.rb new file mode 100644 index 000000000..3a6217ad5 --- /dev/null +++ b/app/cells/cortex/application_cell.rb @@ -0,0 +1,8 @@ +module Cortex + class ApplicationCell < Cell::ViewModel + include ReactOnRailsHelper + include Cortex::Engine.routes.url_helpers + + self.view_paths = ["#{Cortex::Engine.root}/app/cells"] + end +end diff --git a/app/cells/content_type/nav.haml b/app/cells/cortex/content_type/nav.haml similarity index 100% rename from app/cells/content_type/nav.haml rename to app/cells/cortex/content_type/nav.haml diff --git a/app/cells/cortex/content_type_cell.rb b/app/cells/cortex/content_type_cell.rb new file mode 100644 index 000000000..a2fd1a68a --- /dev/null +++ b/app/cells/cortex/content_type_cell.rb @@ -0,0 +1,10 @@ +module Cortex + class ContentTypeCell < Cortex::ApplicationCell + property :name + property :icon + + def nav + render + end + end +end diff --git a/app/cells/cortex/field_cell.rb b/app/cells/cortex/field_cell.rb new file mode 100644 index 000000000..f15a6bc23 --- /dev/null +++ b/app/cells/cortex/field_cell.rb @@ -0,0 +1,35 @@ +module Cortex + class FieldCell < Cortex::ApplicationCell + property :id + property :data + property :field + property :content_item + property :created_at + property :updated_at + property :deleted_at + + def association + raise 'association renderer not implemented' + end + + private + + def render_nested_label(data_property) + @options[:form].label "data[#{data_property}]", field.name do + yield + field.name + end + end + + def render_label + @options[:form].label :data, field.name + end + + def render_tooltip + cell(::Plugins::Core::TooltipCell, nil, tooltip: @options[:tooltip], id: SecureRandom.base64(4)) + end + + def render_field_id + @options[:form].hidden_field :field_id, value: field.id + end + end +end diff --git a/app/cells/index/content_item/column.haml b/app/cells/cortex/index/content_item/column.haml similarity index 100% rename from app/cells/index/content_item/column.haml rename to app/cells/cortex/index/content_item/column.haml diff --git a/app/cells/cortex/index/content_item_cell.rb b/app/cells/cortex/index/content_item_cell.rb new file mode 100644 index 000000000..37b4a4dd6 --- /dev/null +++ b/app/cells/cortex/index/content_item_cell.rb @@ -0,0 +1,37 @@ +module Cortex + module Index + class ContentItemCell < Cortex::ApplicationCell + def column + render + end + + def edit + render + end + + private + + def render_table_data(field) + # TODO: DRY - this logic appears in other classes + if field.has_key?(:id) + @options[:content_item].field_items.find { |fi| fi.field_id == field[:id] }.data.values[0] + elsif field.has_key?(:method) + field[:method].split('.').inject(@options[:content_item], :send) + elsif field.has_key?(:plugin) + cell(field[:plugin][:class_name], plugin_field_item(field), display: field[:plugin][:display], config: field[:plugin][:config]).(field[:plugin][:render_method]) + else + "" + end + end + + def display_classes(display) + display[:classes].join(" ") unless display.nil? + end + + def plugin_field_item(field) + field_id = field[:plugin][:data][:field_id] + @options[:content_item].field_items.find { |field_item| field_item.field_id == field_id } || {} + end + end + end +end diff --git a/app/cells/cortex/index/index.haml b/app/cells/cortex/index/index.haml new file mode 100644 index 000000000..e7c676a9a --- /dev/null +++ b/app/cells/cortex/index/index.haml @@ -0,0 +1,5 @@ +.mdl-grid + .mdl-cell.mdl-cell--12-col + %table.mdl-data-table.mdl-js-data-table.index + = cell(Cortex::IndexCell, @index, data: data).(:table_headers) + = cell(Cortex::IndexCell, @index, data: data).(:table_body) diff --git a/app/cells/index/table_body.haml b/app/cells/cortex/index/table_body.haml similarity index 73% rename from app/cells/index/table_body.haml rename to app/cells/cortex/index/table_body.haml index 43b23d5dc..344198dcb 100644 --- a/app/cells/index/table_body.haml +++ b/app/cells/cortex/index/table_body.haml @@ -5,7 +5,7 @@ %td.mdl-data-table__cell--non-numeric.content_item-link - if context[:popup] = link_to '#', data: { id: content_item.id, title: content_item_title(content_item), thumb: content_item_thumb_url(content_item), url: content_item_asset_url(content_item), alt: content_item_asset_alt_text(content_item), asset_type: content_item_asset_type(content_item) }, class: "media-select--#{context[:popup]}" do - = cell('index/content_item', nil, { cells: column[:cells], content_item: content_item }).(:column) + = cell(Cortex::Index::ContentItemCell, nil, { cells: column[:cells], content_item: content_item }).(:column) - else = link_to edit_content_type_content_item_path(context[:content_type].id, content_item.id) do - = cell('index/content_item', nil, { cells: column[:cells], content_item: content_item }).(:column) + = cell(Cortex::Index::ContentItemCell, nil, { cells: column[:cells], content_item: content_item }).(:column) diff --git a/app/cells/index/table_headers.haml b/app/cells/cortex/index/table_headers.haml similarity index 100% rename from app/cells/index/table_headers.haml rename to app/cells/cortex/index/table_headers.haml diff --git a/app/cells/cortex/index_cell.rb b/app/cells/cortex/index_cell.rb new file mode 100644 index 000000000..ae742ec25 --- /dev/null +++ b/app/cells/cortex/index_cell.rb @@ -0,0 +1,53 @@ +module Cortex + class IndexCell < Cortex::ApplicationCell + property :data + + def index + render + end + + def table_headers + render + end + + def table_body + render + end + + private + + def render_table_header(column_data) + column_data[:name] + end + + def asset_field_item(content_item) + # TODO: This needs to be generic functionality + content_item.field_items.find { |field_item| field_item.field.name == 'Asset' } + end + + def content_item_title(content_item) + # TODO: This needs to be generic functionality + content_item.field_items.find { |field_item| field_item.field.name == 'Title' }.data['text'] + end + + def content_item_thumb_url(content_item) + # TODO: The thumb version needs to be configurable, and this needs to be in a plugin + asset_field_item(content_item).data['asset']&.[]('versions')&.[]('mini')&.[]('url') + end + + def content_item_asset_url(content_item) + # TODO: This needs to be in a plugin + asset_field_item(content_item).data['asset']['versions']['original']['url'] + end + + def content_item_asset_type(content_item) + # TODO: This needs to be in a plugin + MimeMagic.new(asset_field_item(content_item).data['asset']['versions']['original']['mime_type']).mediatype + end + + def content_item_asset_alt_text(content_item) + # TODO: This needs to be in a plugin + content_item.field_items.find { |field_item| field_item.field.name == 'Alt Tag' }.data['text'] + end + end +end diff --git a/app/cells/wizard/column/show.haml b/app/cells/cortex/wizard/column/show.haml similarity index 66% rename from app/cells/wizard/column/show.haml rename to app/cells/cortex/wizard/column/show.haml index 6ffbed172..85e0ca7e8 100644 --- a/app/cells/wizard/column/show.haml +++ b/app/cells/cortex/wizard/column/show.haml @@ -3,8 +3,8 @@ = description - elements.each do |element| - if element.keys.include?("id") - = cell(Wizard::FieldCell, element).() + = cell(Cortex::Wizard::FieldCell, element).() - elsif element.keys.include?("plugin") - = cell(Wizard::PluginCell, nil, plugin_info: element[:plugin]).() + = cell(Cortex::Wizard::PluginCell, nil, plugin_info: element[:plugin]).() - elsif element.keys.include?("method") = "Method is currently unimplemented" diff --git a/app/cells/cortex/wizard/column_cell.rb b/app/cells/cortex/wizard/column_cell.rb new file mode 100644 index 000000000..8fbf50e0e --- /dev/null +++ b/app/cells/cortex/wizard/column_cell.rb @@ -0,0 +1,21 @@ +module Cortex + module Wizard + class ColumnCell < Cortex::ApplicationCell + property :heading + property :grid_width + property :display + property :elements + property :description + + def show + render + end + + private + + def grid_class + "mdl-cell--#{grid_width}-col" + end + end + end +end diff --git a/app/cells/wizard/field/show.haml b/app/cells/cortex/wizard/field/show.haml similarity index 100% rename from app/cells/wizard/field/show.haml rename to app/cells/cortex/wizard/field/show.haml diff --git a/app/cells/cortex/wizard/field_cell.rb b/app/cells/cortex/wizard/field_cell.rb new file mode 100644 index 000000000..eb53447da --- /dev/null +++ b/app/cells/cortex/wizard/field_cell.rb @@ -0,0 +1,29 @@ +module Cortex + module Wizard + class FieldCell < Cortex::ApplicationCell + property :id + property :label + property :input + property :render_method + property :tooltip + + def show + render + end + + private + + def field_item + context[:content_item].field_items.select { |field_item| field_item.field_id == id }[0] + end + + def field + ::Cortex::Field.find_by_id(id) + end + + def field_type + field.field_type_instance + end + end + end +end diff --git a/app/cells/wizard/plugin/show.haml b/app/cells/cortex/wizard/plugin/show.haml similarity index 100% rename from app/cells/wizard/plugin/show.haml rename to app/cells/cortex/wizard/plugin/show.haml diff --git a/app/cells/cortex/wizard/plugin_cell.rb b/app/cells/cortex/wizard/plugin_cell.rb new file mode 100644 index 000000000..cc9763399 --- /dev/null +++ b/app/cells/cortex/wizard/plugin_cell.rb @@ -0,0 +1,28 @@ +module Cortex + module Wizard + class PluginCell < Cortex::ApplicationCell + def show + render + end + + private + + def field_item + field_id = plugin_info[:data][:field_id] + context[:content_item].field_items.find { |field_item| field_item.field_id == field_id } || {} + end + + def display + {} + end + + def plugin_info + @options[:plugin_info] + end + + def cell_information + cell(plugin_info[:class_name], field_item, display: display, config: plugin_info[:config]).(plugin_info[:render_method].to_sym) + end + end + end +end diff --git a/app/cells/cortex/wizard/show.haml b/app/cells/cortex/wizard/show.haml new file mode 100644 index 000000000..06fe7796c --- /dev/null +++ b/app/cells/cortex/wizard/show.haml @@ -0,0 +1 @@ += cell(Cortex::Wizard::StepCell, collection: data_mash.steps).() diff --git a/app/cells/cortex/wizard/step/show.haml b/app/cells/cortex/wizard/step/show.haml new file mode 100644 index 000000000..c58a10918 --- /dev/null +++ b/app/cells/cortex/wizard/step/show.haml @@ -0,0 +1,2 @@ +.mdl-grid + = cell(Cortex::Wizard::ColumnCell, collection: columns).() diff --git a/app/cells/cortex/wizard/step_cell.rb b/app/cells/cortex/wizard/step_cell.rb new file mode 100644 index 000000000..bfb06b981 --- /dev/null +++ b/app/cells/cortex/wizard/step_cell.rb @@ -0,0 +1,17 @@ +module Cortex + module Wizard + class StepCell < Cortex::ApplicationCell + include ActionView::RecordIdentifier + include ActionView::Helpers::FormHelper + + property :name + property :heading + property :description + property :columns + + def show + render + end + end + end +end diff --git a/app/cells/cortex/wizard_cell.rb b/app/cells/cortex/wizard_cell.rb new file mode 100644 index 000000000..dccf8d43b --- /dev/null +++ b/app/cells/cortex/wizard_cell.rb @@ -0,0 +1,15 @@ +module Cortex + class WizardCell < Cortex::ApplicationCell + property :data + + def show + render + end + + private + + def data_mash + Hashie::Mash.new(data) + end + end +end diff --git a/app/cells/field_cell.rb b/app/cells/field_cell.rb deleted file mode 100644 index 3100b0824..000000000 --- a/app/cells/field_cell.rb +++ /dev/null @@ -1,33 +0,0 @@ -class FieldCell < Cell::ViewModel - property :id - property :data - property :field - property :content_item - property :created_at - property :updated_at - property :deleted_at - - def association - raise 'association renderer not implemented' - end - - private - - def render_nested_label(data_property) - @options[:form].label "data[#{data_property}]", field.name do - yield + field.name - end - end - - def render_label - @options[:form].label :data, field.name - end - - def render_tooltip - cell(Plugins::Core::TooltipCell, nil, tooltip: @options[:tooltip], id: SecureRandom.base64(4)) - end - - def render_field_id - @options[:form].hidden_field :field_id, value: field.id - end -end diff --git a/app/cells/index/content_item_cell.rb b/app/cells/index/content_item_cell.rb deleted file mode 100644 index 422cff250..000000000 --- a/app/cells/index/content_item_cell.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Index - class ContentItemCell < Cell::ViewModel - def column - render - end - - def edit - render - end - - private - - def render_table_data(field) - # TODO: DRY - this logic appears in other classes - if field.has_key?(:id) - @options[:content_item].field_items.find { |fi| fi.field_id == field[:id] }.data.values[0] - elsif field.has_key?(:method) - field[:method].split('.').inject(@options[:content_item], :send) - elsif field.has_key?(:plugin) - cell(field[:plugin][:class_name], plugin_field_item(field), display: field[:plugin][:display], config: field[:plugin][:config]).(field[:plugin][:render_method]) - else - "" - end - end - - def display_classes(display) - display[:classes].join(" ") unless display.nil? - end - - def plugin_field_item(field) - field_id = field[:plugin][:data][:field_id] - @options[:content_item].field_items.find { |field_item| field_item.field_id == field_id } || {} - end - end -end diff --git a/app/cells/index/index.haml b/app/cells/index/index.haml deleted file mode 100644 index 54acc0499..000000000 --- a/app/cells/index/index.haml +++ /dev/null @@ -1,5 +0,0 @@ -.mdl-grid - .mdl-cell.mdl-cell--12-col - %table.mdl-data-table.mdl-js-data-table.index - = cell('index', @index, data: data).(:table_headers) - = cell('index', @index, data: data).(:table_body) diff --git a/app/cells/index_cell.rb b/app/cells/index_cell.rb deleted file mode 100644 index ead2f2593..000000000 --- a/app/cells/index_cell.rb +++ /dev/null @@ -1,51 +0,0 @@ -class IndexCell < Cell::ViewModel - property :data - - def index - render - end - - def table_headers - render - end - - def table_body - render - end - - private - - def render_table_header(column_data) - column_data[:name] - end - - def asset_field_item(content_item) - # TODO: This needs to be generic functionality - content_item.field_items.find { |field_item| field_item.field.name == 'Asset' } - end - - def content_item_title(content_item) - # TODO: This needs to be generic functionality - content_item.field_items.find { |field_item| field_item.field.name == 'Title' }.data['text'] - end - - def content_item_thumb_url(content_item) - # TODO: The thumb version needs to be configurable, and this needs to be in a plugin - asset_field_item(content_item).data['asset']&.[]('versions')&.[]('mini')&.[]('url') - end - - def content_item_asset_url(content_item) - # TODO: This needs to be in a plugin - asset_field_item(content_item).data['asset']['versions']['original']['url'] - end - - def content_item_asset_type(content_item) - # TODO: This needs to be in a plugin - MimeMagic.new(asset_field_item(content_item).data['asset']['versions']['original']['mime_type']).mediatype - end - - def content_item_asset_alt_text(content_item) - # TODO: This needs to be in a plugin - content_item.field_items.find { |field_item| field_item.field.name == 'Alt Tag' }.data['text'] - end -end diff --git a/app/cells/wizard/column_cell.rb b/app/cells/wizard/column_cell.rb deleted file mode 100644 index d43b582ee..000000000 --- a/app/cells/wizard/column_cell.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Wizard - class ColumnCell < Cell::ViewModel - property :heading - property :grid_width - property :display - property :elements - property :description - - def show - render - end - - private - - def grid_class - "mdl-cell--#{grid_width}-col" - end - end -end diff --git a/app/cells/wizard/field_cell.rb b/app/cells/wizard/field_cell.rb deleted file mode 100644 index 12e0934cb..000000000 --- a/app/cells/wizard/field_cell.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Wizard - class FieldCell < Cell::ViewModel - property :id - property :label - property :input - property :render_method - property :tooltip - - def show - render - end - - private - - def field_item - context[:content_item].field_items.select { |field_item| field_item.field_id == id }[0] - end - - def field - ::Field.find_by_id(id) - end - - def field_type - field.field_type_instance - end - end -end diff --git a/app/cells/wizard/plugin_cell.rb b/app/cells/wizard/plugin_cell.rb deleted file mode 100644 index 392a24fc7..000000000 --- a/app/cells/wizard/plugin_cell.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Wizard - class PluginCell < Cell::ViewModel - def show - render - end - - private - - def field_item - field_id = plugin_info[:data][:field_id] - context[:content_item].field_items.find { |field_item| field_item.field_id == field_id } || {} - end - - def display - {} - end - - def plugin_info - @options[:plugin_info] - end - - def cell_information - cell(plugin_info[:class_name], field_item, display: display, config: plugin_info[:config]).(plugin_info[:render_method].to_sym) - end - end -end diff --git a/app/cells/wizard/show.haml b/app/cells/wizard/show.haml deleted file mode 100644 index d80a10001..000000000 --- a/app/cells/wizard/show.haml +++ /dev/null @@ -1 +0,0 @@ -= cell(Wizard::StepCell, collection: data_mash.steps).() diff --git a/app/cells/wizard/step/show.haml b/app/cells/wizard/step/show.haml deleted file mode 100644 index 9af67cf65..000000000 --- a/app/cells/wizard/step/show.haml +++ /dev/null @@ -1,2 +0,0 @@ -.mdl-grid - = cell(Wizard::ColumnCell, collection: columns).() diff --git a/app/cells/wizard/step_cell.rb b/app/cells/wizard/step_cell.rb deleted file mode 100644 index d7d7d20d7..000000000 --- a/app/cells/wizard/step_cell.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Wizard - class StepCell < Cell::ViewModel - include ActionView::RecordIdentifier - include ActionView::Helpers::FormHelper - - property :name - property :heading - property :description - property :columns - - def show - render - end - end -end diff --git a/app/cells/wizard_cell.rb b/app/cells/wizard_cell.rb deleted file mode 100644 index 779a1cf17..000000000 --- a/app/cells/wizard_cell.rb +++ /dev/null @@ -1,13 +0,0 @@ -class WizardCell < Cell::ViewModel - property :data - - def show - render - end - - private - - def data_mash - Hashie::Mash.new(data) - end -end diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb deleted file mode 100644 index 8bea17fdf..000000000 --- a/app/controllers/admin_controller.rb +++ /dev/null @@ -1,3 +0,0 @@ -class AdminController < ApplicationController - before_action :authenticate_user! -end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb deleted file mode 100644 index c1af13e18..000000000 --- a/app/controllers/application_controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -class ApplicationController < ActionController::Base - protect_from_forgery - before_action :default_headers - before_action :configure_permitted_parameters, if: :devise_controller? - respond_to :html, :json - add_flash_types :success, :warning, :danger, :info - - protected - - def default_headers - headers['X-UA-Compatible'] = 'IE=edge' - end - - def configure_permitted_parameters - devise_parameter_sanitizer.permit(:sign_up, keys: [:tenant_id]) - end -end diff --git a/app/controllers/authentication/passwords_controller.rb b/app/controllers/authentication/passwords_controller.rb deleted file mode 100644 index 24ebee4df..000000000 --- a/app/controllers/authentication/passwords_controller.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Authentication::PasswordsController < Devise::PasswordsController -end diff --git a/app/controllers/authentication/sessions_controller.rb b/app/controllers/authentication/sessions_controller.rb deleted file mode 100644 index 35997e3b5..000000000 --- a/app/controllers/authentication/sessions_controller.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Authentication::SessionsController < Devise::SessionsController -end diff --git a/app/controllers/content_items_controller.rb b/app/controllers/content_items_controller.rb deleted file mode 100644 index 5dd0a9bfb..000000000 --- a/app/controllers/content_items_controller.rb +++ /dev/null @@ -1,70 +0,0 @@ -class ContentItemsController < AdminController - include ContentItemHelper - include PopupHelper - - def index - @index = IndexDecoratorService.new(content_type: content_type) - @content_items = content_type.content_items.find_by_tenant(current_user.active_tenant) - add_breadcrumb content_type.name.pluralize - end - - def new - @content_item = content_type.content_items.new - content_type.fields.each do |field| - # TODO: Should this hit the Plugin Transaction Layer? - @content_item.field_items << FieldItem.new(field: field) - end - @wizard = WizardDecoratorService.new(content_item: @content_item) - - add_breadcrumb content_type.name.pluralize, :content_type_content_items_path - add_breadcrumb 'New' - end - - def edit - @content_item = content_type.content_items.find_by_tenant(current_user.active_tenant).find_by_id(params[:id]) - @wizard = WizardDecoratorService.new(content_item: @content_item) - - title = @content_item.field_items.find { |field_item| field_item.field.name == 'Title' }.data['text'] # TODO: refactor this hardcoded Field reference - add_breadcrumb content_type.name.pluralize, :content_type_content_items_path - add_breadcrumb title - add_breadcrumb 'Edit' - end - - def update - begin - content_item.update - rescue ActiveRecord::RecordInvalid => e - flash[:warning] = validation_message(e.message) - @content_item = content_item_reload(content_type.content_items.find_by_id(params[:id])) - @wizard = WizardDecoratorService.new(content_item: @content_item) - - title = @content_item.field_items.find { |field_item| field_item.field.name == 'Title' }.data['text'] # TODO: refactor this hardcoded Field reference - add_breadcrumb content_type.name.pluralize, :content_type_content_items_path - add_breadcrumb title - add_breadcrumb 'Edit' - - render :edit - else - flash[:success] = "Hooray! #{content_type.name} Updated!" - redirect_to content_type_content_items_path - end - end - - def create - begin - content_item.create - rescue ActiveRecord::RecordInvalid => e - flash[:warning] = validation_message(e.message) - @content_item = content_item_reload(content_type.content_items.new) - @wizard = WizardDecoratorService.new(content_item: @content_item) - - add_breadcrumb content_type.name.pluralize, :content_type_content_items_path - add_breadcrumb 'New' - - render :new - else - flash[:success] = "Hooray! #{content_type.name} Created!" - redirect_to content_type_content_items_path - end - end -end diff --git a/app/controllers/content_types_controller.rb b/app/controllers/content_types_controller.rb deleted file mode 100644 index 15b7f3cd9..000000000 --- a/app/controllers/content_types_controller.rb +++ /dev/null @@ -1,15 +0,0 @@ -class ContentTypesController < AdminController - add_breadcrumb 'Content Types', :content_types_path - - def index - @content_types = ContentType.all - end - - def new - - end - - def create - - end -end diff --git a/app/controllers/cortex/admin_controller.rb b/app/controllers/cortex/admin_controller.rb new file mode 100644 index 000000000..f9449b406 --- /dev/null +++ b/app/controllers/cortex/admin_controller.rb @@ -0,0 +1,7 @@ +require_dependency 'cortex/application_controller' + +module Cortex + class AdminController < ApplicationController + before_action :authenticate_user! + end +end diff --git a/app/controllers/cortex/application_controller.rb b/app/controllers/cortex/application_controller.rb new file mode 100644 index 000000000..1ff0e5a93 --- /dev/null +++ b/app/controllers/cortex/application_controller.rb @@ -0,0 +1,19 @@ +module Cortex + class ApplicationController < ActionController::Base + protect_from_forgery with: :exception + before_action :default_headers + before_action :configure_permitted_parameters, if: :devise_controller? + respond_to :html, :json + add_flash_types :success, :warning, :danger, :info + + protected + + def default_headers + headers['X-UA-Compatible'] = 'IE=edge' + end + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: [:tenant_id]) + end + end +end diff --git a/app/controllers/cortex/authentication/passwords_controller.rb b/app/controllers/cortex/authentication/passwords_controller.rb new file mode 100644 index 000000000..821327f8f --- /dev/null +++ b/app/controllers/cortex/authentication/passwords_controller.rb @@ -0,0 +1,3 @@ +class Cortex::Authentication::PasswordsController < Devise::PasswordsController + layout 'cortex/devise' +end diff --git a/app/controllers/cortex/authentication/sessions_controller.rb b/app/controllers/cortex/authentication/sessions_controller.rb new file mode 100644 index 000000000..9386e2f7d --- /dev/null +++ b/app/controllers/cortex/authentication/sessions_controller.rb @@ -0,0 +1,3 @@ +class Cortex::Authentication::SessionsController < Devise::SessionsController + layout 'cortex/devise' +end diff --git a/app/controllers/cortex/content_items_controller.rb b/app/controllers/cortex/content_items_controller.rb new file mode 100644 index 000000000..737875c93 --- /dev/null +++ b/app/controllers/cortex/content_items_controller.rb @@ -0,0 +1,74 @@ +require_dependency 'cortex/application_controller' + +module Cortex + class ContentItemsController < AdminController + include ContentItemHelper + include PopupHelper + + def index + @index = IndexDecoratorService.new(content_type: content_type) + @content_items = content_type.content_items.find_by_tenant(current_user.active_tenant) + add_breadcrumb content_type.name.pluralize + end + + def new + @content_item = content_type.content_items.new + content_type.fields.each do |field| + # TODO: Should this hit the Plugin Transaction Layer? + @content_item.field_items << FieldItem.new(field: field) + end + @wizard = WizardDecoratorService.new(content_item: @content_item) + + add_breadcrumb content_type.name.pluralize, :content_type_content_items_path + add_breadcrumb 'New' + end + + def edit + @content_item = content_type.content_items.find_by_tenant(current_user.active_tenant).find_by_id(params[:id]) + @wizard = WizardDecoratorService.new(content_item: @content_item) + + title = @content_item.field_items.find { |field_item| field_item.field.name == 'Title' }.data['text'] # TODO: refactor this hardcoded Field reference + add_breadcrumb content_type.name.pluralize, :content_type_content_items_path + add_breadcrumb title + add_breadcrumb 'Edit' + end + + def update + begin + content_item.update + rescue ActiveRecord::RecordInvalid => e + flash[:warning] = validation_message(e.message) + @content_item = content_item_reload(content_type.content_items.find_by_id(params[:id])) + @wizard = WizardDecoratorService.new(content_item: @content_item) + + title = @content_item.field_items.find { |field_item| field_item.field.name == 'Title' }.data['text'] # TODO: refactor this hardcoded Field reference + add_breadcrumb content_type.name.pluralize, :content_type_content_items_path + add_breadcrumb title + add_breadcrumb 'Edit' + + render :edit + else + flash[:success] = "Hooray! #{content_type.name} Updated!" + redirect_to content_type_content_items_path + end + end + + def create + begin + content_item.create + rescue ActiveRecord::RecordInvalid => e + flash[:warning] = validation_message(e.message) + @content_item = content_item_reload(content_type.content_items.new) + @wizard = WizardDecoratorService.new(content_item: @content_item) + + add_breadcrumb content_type.name.pluralize, :content_type_content_items_path + add_breadcrumb 'New' + + render :new + else + flash[:success] = "Hooray! #{content_type.name} Created!" + redirect_to content_type_content_items_path + end + end + end +end diff --git a/app/controllers/cortex/content_types_controller.rb b/app/controllers/cortex/content_types_controller.rb new file mode 100644 index 000000000..f5bead58b --- /dev/null +++ b/app/controllers/cortex/content_types_controller.rb @@ -0,0 +1,19 @@ +require_dependency 'cortex/application_controller' + +module Cortex + class ContentTypesController < AdminController + add_breadcrumb 'Content Types', :content_types_path + + def index + @content_types = Cortex::ContentType.all + end + + def new + + end + + def create + + end + end +end diff --git a/app/controllers/cortex/dashboards_controller.rb b/app/controllers/cortex/dashboards_controller.rb new file mode 100644 index 000000000..db86b72de --- /dev/null +++ b/app/controllers/cortex/dashboards_controller.rb @@ -0,0 +1,7 @@ +require_dependency 'cortex/application_controller' + +module Cortex + class DashboardsController < AdminController + add_breadcrumb 'Dashboard', :dashboards_path + end +end diff --git a/app/controllers/cortex/graphql_controller.rb b/app/controllers/cortex/graphql_controller.rb new file mode 100644 index 000000000..835f711de --- /dev/null +++ b/app/controllers/cortex/graphql_controller.rb @@ -0,0 +1,40 @@ +require_dependency 'cortex/application_controller' + +module Cortex + class GraphqlController < ApplicationController + def execute + variables = ensure_hash(params[:variables]) + query = params[:query] + operation_name = params[:operationName] + context = { + current_user: current_user + } + result = Cortex::CortexSchema.execute(query, variables: variables, context: context, operation_name: operation_name) + render json: result + end + + def ide + add_breadcrumb 'GraphQL IDE', :graphql_ide_path + end + + private + + # Handle form data, JSON body, or a blank value + def ensure_hash(ambiguous_param) + case ambiguous_param + when String + if ambiguous_param.present? + ensure_hash(JSON.parse(ambiguous_param)) + else + {} + end + when Hash, ActionController::Parameters + ambiguous_param + when nil + {} + else + raise ArgumentError, "Unexpected parameter: #{ambiguous_param}" + end + end + end +end diff --git a/app/controllers/cortex/rss/v2/rss_controller.rb b/app/controllers/cortex/rss/v2/rss_controller.rb new file mode 100644 index 000000000..8c067d144 --- /dev/null +++ b/app/controllers/cortex/rss/v2/rss_controller.rb @@ -0,0 +1,15 @@ +require_dependency 'cortex/application_controller' + +module Cortex + module Rss + module V2 + class RssController < ApplicationController + include RssHelper + + def index + @content_items = rss_content_type.content_items.select { |content_item| content_item.published? } + end + end + end + end +end diff --git a/app/controllers/cortex/tenants_controller.rb b/app/controllers/cortex/tenants_controller.rb new file mode 100644 index 000000000..fa2b22796 --- /dev/null +++ b/app/controllers/cortex/tenants_controller.rb @@ -0,0 +1,12 @@ +require_dependency 'cortex/application_controller' + +module Cortex + class TenantsController < AdminController + def switch_tenants + current_user.update(active_tenant: Tenant.find(params[:requested_tenant])) + respond_to do |format| + format.json { render :json => current_user.active_tenant } + end + end + end +end diff --git a/app/controllers/dashboards_controller.rb b/app/controllers/dashboards_controller.rb deleted file mode 100644 index f7c029eed..000000000 --- a/app/controllers/dashboards_controller.rb +++ /dev/null @@ -1,3 +0,0 @@ -class DashboardsController < AdminController - add_breadcrumb 'Dashboard', :dashboards_path -end diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb deleted file mode 100644 index 7a9db8dda..000000000 --- a/app/controllers/graphql_controller.rb +++ /dev/null @@ -1,36 +0,0 @@ -class GraphqlController < ApplicationController - def execute - variables = ensure_hash(params[:variables]) - query = params[:query] - operation_name = params[:operationName] - context = { - current_user: current_user - } - result = CortexSchema.execute(query, variables: variables, context: context, operation_name: operation_name) - render json: result - end - - def ide - add_breadcrumb 'GraphQL IDE', :graphql_ide_path - end - - private - - # Handle form data, JSON body, or a blank value - def ensure_hash(ambiguous_param) - case ambiguous_param - when String - if ambiguous_param.present? - ensure_hash(JSON.parse(ambiguous_param)) - else - {} - end - when Hash, ActionController::Parameters - ambiguous_param - when nil - {} - else - raise ArgumentError, "Unexpected parameter: #{ambiguous_param}" - end - end -end diff --git a/app/controllers/rss/v2/rss_controller.rb b/app/controllers/rss/v2/rss_controller.rb deleted file mode 100644 index f87b4f050..000000000 --- a/app/controllers/rss/v2/rss_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Rss - module V2 - class RssController < ApplicationController - include RssHelper - - def index - @content_items = rss_content_type.content_items.select { |content_item| content_item.published? } - end - end - end -end diff --git a/app/controllers/tenants_controller.rb b/app/controllers/tenants_controller.rb deleted file mode 100644 index 457265a28..000000000 --- a/app/controllers/tenants_controller.rb +++ /dev/null @@ -1,8 +0,0 @@ -class TenantsController < AdminController - def switch_tenants - current_user.update(active_tenant: Tenant.find(params[:requested_tenant])) - respond_to do |format| - format.json { render :json => current_user.active_tenant } - end - end -end diff --git a/app/devise/authentication_failure.rb b/app/devise/authentication_failure.rb deleted file mode 100644 index 24cb0e76c..000000000 --- a/app/devise/authentication_failure.rb +++ /dev/null @@ -1,10 +0,0 @@ -class AuthenticationFailure < Devise::FailureApp - # You need to override respond to eliminate recall - def respond - if http_auth? - http_auth - else - redirect - end - end -end diff --git a/app/devise/cortex/authentication_failure.rb b/app/devise/cortex/authentication_failure.rb new file mode 100644 index 000000000..9a63f4108 --- /dev/null +++ b/app/devise/cortex/authentication_failure.rb @@ -0,0 +1,12 @@ +module Cortex + class AuthenticationFailure < Devise::FailureApp + # You need to override respond to eliminate recall + def respond + if http_auth? + http_auth + else + redirect + end + end + end +end diff --git a/app/graphql/cortex/cortex_schema.rb b/app/graphql/cortex/cortex_schema.rb new file mode 100644 index 000000000..5e71ee4c4 --- /dev/null +++ b/app/graphql/cortex/cortex_schema.rb @@ -0,0 +1,8 @@ +module Cortex + CortexSchema = GraphQL::Schema.define do + use ApolloTracing.new + + # mutation(Types::MutationType) + query(Types::QueryType) + end +end diff --git a/app/graphql/interfaces/.keep b/app/graphql/cortex/interfaces/.keep similarity index 100% rename from app/graphql/interfaces/.keep rename to app/graphql/cortex/interfaces/.keep diff --git a/app/graphql/mutations/.keep b/app/graphql/cortex/mutations/.keep similarity index 100% rename from app/graphql/mutations/.keep rename to app/graphql/cortex/mutations/.keep diff --git a/app/graphql/cortex/types/content_item_type.rb b/app/graphql/cortex/types/content_item_type.rb new file mode 100644 index 000000000..1d389c902 --- /dev/null +++ b/app/graphql/cortex/types/content_item_type.rb @@ -0,0 +1,31 @@ +module Cortex + Types::ContentItemType = GraphQL::ObjectType.define do + name 'ContentItem' + description 'Content created by a Content Creator from a ContentType' + + field :id, !types.ID + field :state, types.String + field :tenant, !Types::TenantType + field :creator, !Types::UserType + field :content_type, !Types::ContentTypeType + + field :updated_by, Types::UserType + field :created_at, !Types::DateTimeType + field :updated_at, !Types::DateTimeType + field :deleted_at, Types::DateTimeType + + field :allFieldItems, types[Types::FieldItemType] do + resolve -> (obj, _args, _ctx) { + obj.field_items + } + end + + field :FieldItem, Types::FieldItemType do + argument :name_id, !types.String + + resolve -> (obj, args, _ctx) { + obj.field_items.find { |field_item| field_item.field.name_id == args[:name_id] } + } + end + end +end diff --git a/app/graphql/cortex/types/content_type_type.rb b/app/graphql/cortex/types/content_type_type.rb new file mode 100644 index 000000000..76448a438 --- /dev/null +++ b/app/graphql/cortex/types/content_type_type.rb @@ -0,0 +1,23 @@ +module Cortex + Types::ContentTypeType = GraphQL::ObjectType.define do + name 'ContentType' + description 'Content model set forth by a superadministrator. ContentItems are created from this.' + + field :id, !types.ID + field :name, !types.String + field :name_id, !types.String + field :description, types.String + field :publishable, !types.Boolean + field :icon, types.String + field :contract, !Types::ContractType + field :tenant, !Types::TenantType + field :creator, !Types::UserType + + field :updated_by, Types::UserType + field :created_at, !Types::DateTimeType + field :updated_at, !Types::DateTimeType + field :deleted_at, Types::DateTimeType + + # TODO: Fields + end +end diff --git a/app/graphql/cortex/types/contract_type.rb b/app/graphql/cortex/types/contract_type.rb new file mode 100644 index 000000000..46d12b58c --- /dev/null +++ b/app/graphql/cortex/types/contract_type.rb @@ -0,0 +1,10 @@ +module Cortex + Types::ContractType = GraphQL::ObjectType.define do + name 'Contract' + description '' + + field :id, !types.ID + + # TODO: the whole thing + end +end diff --git a/app/graphql/cortex/types/date_time_type.rb b/app/graphql/cortex/types/date_time_type.rb new file mode 100644 index 000000000..2d7a67a0a --- /dev/null +++ b/app/graphql/cortex/types/date_time_type.rb @@ -0,0 +1,8 @@ +module Cortex + Types::DateTimeType = GraphQL::ScalarType.define do + name 'DateTime' + + coerce_input ->(value, _ctx) { Time.zone.parse(value) } + coerce_result ->(value, _ctx) { value.utc.iso8601 } + end +end diff --git a/app/graphql/cortex/types/field_item_type.rb b/app/graphql/cortex/types/field_item_type.rb new file mode 100644 index 000000000..c0159b373 --- /dev/null +++ b/app/graphql/cortex/types/field_item_type.rb @@ -0,0 +1,15 @@ +module Cortex + Types::FieldItemType = GraphQL::ObjectType.define do + name 'FieldItem' + description 'Individual fields storing content that make up a ContentItem' + + field :id, !types.ID + field :data, !types.String + field :field, !Types::FieldType + field :content_item, !Types::ContentItemType + + field :created_at, !Types::DateTimeType + field :updated_at, !Types::DateTimeType + field :deleted_at, Types::DateTimeType + end +end diff --git a/app/graphql/cortex/types/field_type.rb b/app/graphql/cortex/types/field_type.rb new file mode 100644 index 000000000..e3b2c56f9 --- /dev/null +++ b/app/graphql/cortex/types/field_type.rb @@ -0,0 +1,18 @@ +module Cortex + Types::FieldType = GraphQL::ObjectType.define do + name 'Field' + description 'Configured FieldTypes that make up a ContentType' + + field :id, !types.ID + field :name, !types.String + field :name_id, !types.String + field :field_type, !types.String + field :metadata, !types.String + field :validations, !types.String + field :content_type, !Types::ContentTypeType + + field :created_at, !Types::DateTimeType + field :updated_at, !Types::DateTimeType + field :deleted_at, Types::DateTimeType + end +end diff --git a/app/graphql/cortex/types/mutation_type.rb b/app/graphql/cortex/types/mutation_type.rb new file mode 100644 index 000000000..ed3b1164f --- /dev/null +++ b/app/graphql/cortex/types/mutation_type.rb @@ -0,0 +1,5 @@ +module Cortex + Types::MutationType = GraphQL::ObjectType.define do + name 'Mutation' + end +end diff --git a/app/graphql/cortex/types/query_type.rb b/app/graphql/cortex/types/query_type.rb new file mode 100644 index 000000000..9be8a5eff --- /dev/null +++ b/app/graphql/cortex/types/query_type.rb @@ -0,0 +1,44 @@ +module Cortex + Types::QueryType = GraphQL::ObjectType.define do + name 'Query' + + field :currentUser, !Types::UserType do + resolve ->(_obj, _args, ctx) { + ctx[:current_user] + } + end + + field :allUsers, types[Types::UserType] do + resolve -> (_obj, _args, _ctx) { + # TODO: scope to tenant; allow tenant argument + Cortex::User.all + } + end + + # TODO: extract to class + Cortex::ContentType.find_each do |content_type| + all_field_for_content_type(content_type) + # field_for_content_type(content_type) + # ... + end + end + + def all_field_for_content_type(content_type) + field(field_name('all', content_type), types[Types::ContentItemType]) do + argument :page, types.Int + argument :limit, types.Int + argument :tenant_id, types.ID # TODO: check that tenant_id within active_tenant and authorize. Which layer should this occur in? + + description content_type.description + + resolve -> (_obj, args, ctx) { + params = { args: args, active_tenant: ctx[:current_user].active_tenant, content_type: content_type } + Cortex::GetContentItemsForContentTypeTransaction.new.call(params).value + } + end + end + + def field_name(prefix, content_type) # TODO: extract + prefix + content_type.name_id.camelize.pluralize + end +end diff --git a/app/graphql/cortex/types/tenant_type.rb b/app/graphql/cortex/types/tenant_type.rb new file mode 100644 index 000000000..aede1f40c --- /dev/null +++ b/app/graphql/cortex/types/tenant_type.rb @@ -0,0 +1,19 @@ +module Cortex + Types::TenantType = GraphQL::ObjectType.define do + name 'Tenant' + description 'Tenants segregate and nest users and content' + + field :id, !types.ID + field :name, !types.String + field :name_id, !types.String + field :description, !types.String + field :parent, types[Types::TenantType] + field :children, types[Types::TenantType] + field :owner, !types[Types::UserType] + field :created_at, !Types::DateTimeType + field :updated_at, !Types::DateTimeType + field :deleted_at, Types::DateTimeType + + # TODO: Owned ContentItems, etc + end +end diff --git a/app/graphql/cortex/types/user_type.rb b/app/graphql/cortex/types/user_type.rb new file mode 100644 index 000000000..133f7c6b3 --- /dev/null +++ b/app/graphql/cortex/types/user_type.rb @@ -0,0 +1,20 @@ +module Cortex + Types::UserType = GraphQL::ObjectType.define do + name 'User' + description 'Cortex administrator or content creator' + + field :id, !types.ID + field :firstname, !types.String + field :lastname, !types.String + field :locale, !types.String + field :timezone, !types.String + field :created_at, !Types::DateTimeType + field :updated_at, !Types::DateTimeType + field :deleted_at, Types::DateTimeType + + field :tenants, !types[Types::TenantType] + field :active_tenant, !types[Types::TenantType] + + # TODO: Owned ContentItems, etc + end +end diff --git a/app/graphql/cortex_schema.rb b/app/graphql/cortex_schema.rb deleted file mode 100644 index a682b276e..000000000 --- a/app/graphql/cortex_schema.rb +++ /dev/null @@ -1,6 +0,0 @@ -CortexSchema = GraphQL::Schema.define do - use ApolloTracing.new - - # mutation(Types::MutationType) - query(Types::QueryType) -end diff --git a/app/graphql/types/content_item_type.rb b/app/graphql/types/content_item_type.rb deleted file mode 100644 index 6cefac1e4..000000000 --- a/app/graphql/types/content_item_type.rb +++ /dev/null @@ -1,29 +0,0 @@ -Types::ContentItemType = GraphQL::ObjectType.define do - name 'ContentItem' - description 'Content created by a Content Creator from a ContentType' - - field :id, !types.ID - field :state, types.String - field :tenant, !Types::TenantType - field :creator, !Types::UserType - field :content_type, !Types::ContentTypeType - - field :updated_by, Types::UserType - field :created_at, !Types::DateTimeType - field :updated_at, !Types::DateTimeType - field :deleted_at, Types::DateTimeType - - field :allFieldItems, types[Types::FieldItemType] do - resolve -> (obj, _args, _ctx) { - obj.field_items - } - end - - field :FieldItem, Types::FieldItemType do - argument :name_id, !types.String - - resolve -> (obj, args, _ctx) { - obj.field_items.find { |field_item| field_item.field.name_id == args[:name_id] } - } - end -end diff --git a/app/graphql/types/content_type_type.rb b/app/graphql/types/content_type_type.rb deleted file mode 100644 index 26f21cc27..000000000 --- a/app/graphql/types/content_type_type.rb +++ /dev/null @@ -1,21 +0,0 @@ -Types::ContentTypeType = GraphQL::ObjectType.define do - name 'ContentType' - description 'Content model set forth by a superadministrator. ContentItems are created from this.' - - field :id, !types.ID - field :name, !types.String - field :name_id, !types.String - field :description, types.String - field :publishable, !types.Boolean - field :icon, types.String - field :contract, !Types::ContractType - field :tenant, !Types::TenantType - field :creator, !Types::UserType - - field :updated_by, Types::UserType - field :created_at, !Types::DateTimeType - field :updated_at, !Types::DateTimeType - field :deleted_at, Types::DateTimeType - - # TODO: Fields -end diff --git a/app/graphql/types/contract_type.rb b/app/graphql/types/contract_type.rb deleted file mode 100644 index 1235cf049..000000000 --- a/app/graphql/types/contract_type.rb +++ /dev/null @@ -1,8 +0,0 @@ -Types::ContractType = GraphQL::ObjectType.define do - name 'Contract' - description '' - - field :id, !types.ID - - # TODO: the whole thing -end diff --git a/app/graphql/types/date_time_type.rb b/app/graphql/types/date_time_type.rb deleted file mode 100644 index 153d45b9a..000000000 --- a/app/graphql/types/date_time_type.rb +++ /dev/null @@ -1,6 +0,0 @@ -Types::DateTimeType = GraphQL::ScalarType.define do - name 'DateTime' - - coerce_input ->(value, _ctx) { Time.zone.parse(value) } - coerce_result ->(value, _ctx) { value.utc.iso8601 } -end diff --git a/app/graphql/types/field_item_type.rb b/app/graphql/types/field_item_type.rb deleted file mode 100644 index 012984287..000000000 --- a/app/graphql/types/field_item_type.rb +++ /dev/null @@ -1,13 +0,0 @@ -Types::FieldItemType = GraphQL::ObjectType.define do - name 'FieldItem' - description 'Individual fields storing content that make up a ContentItem' - - field :id, !types.ID - field :data, !types.String - field :field, !Types::FieldType - field :content_item, !Types::ContentItemType - - field :created_at, !Types::DateTimeType - field :updated_at, !Types::DateTimeType - field :deleted_at, Types::DateTimeType -end diff --git a/app/graphql/types/field_type.rb b/app/graphql/types/field_type.rb deleted file mode 100644 index e56189748..000000000 --- a/app/graphql/types/field_type.rb +++ /dev/null @@ -1,16 +0,0 @@ -Types::FieldType = GraphQL::ObjectType.define do - name 'Field' - description 'Configured FieldTypes that make up a ContentType' - - field :id, !types.ID - field :name, !types.String - field :name_id, !types.String - field :field_type, !types.String - field :metadata, !types.String - field :validations, !types.String - field :content_type, !Types::ContentTypeType - - field :created_at, !Types::DateTimeType - field :updated_at, !Types::DateTimeType - field :deleted_at, Types::DateTimeType -end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb deleted file mode 100644 index 396afc8b1..000000000 --- a/app/graphql/types/mutation_type.rb +++ /dev/null @@ -1,3 +0,0 @@ -Types::MutationType = GraphQL::ObjectType.define do - name 'Mutation' -end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb deleted file mode 100644 index 649fe6007..000000000 --- a/app/graphql/types/query_type.rb +++ /dev/null @@ -1,42 +0,0 @@ -Types::QueryType = GraphQL::ObjectType.define do - name 'Query' - - field :currentUser, !Types::UserType do - resolve ->(_obj, _args, ctx) { - ctx[:current_user] - } - end - - field :allUsers, types[Types::UserType] do - resolve -> (_obj, _args, _ctx) { - # TODO: scope to tenant; allow tenant argument - User.all - } - end - - # TODO: extract to class - ContentType.find_each do |content_type| - all_field_for_content_type(content_type) - # field_for_content_type(content_type) - # ... - end -end - -def all_field_for_content_type(content_type) - field(field_name('all', content_type), types[Types::ContentItemType]) do - argument :page, types.Int - argument :limit, types.Int - argument :tenant_id, types.ID # TODO: check that tenant_id within active_tenant and authorize. Which layer should this occur in? - - description content_type.description - - resolve -> (_obj, args, ctx) { - params = { args: args, active_tenant: ctx[:current_user].active_tenant, content_type: content_type } - GetContentItemsForContentTypeTransaction.new.call(params).value - } - end -end - -def field_name(prefix, content_type) # TODO: extract - prefix + content_type.name_id.camelize.pluralize -end diff --git a/app/graphql/types/tenant_type.rb b/app/graphql/types/tenant_type.rb deleted file mode 100644 index 03c2d1002..000000000 --- a/app/graphql/types/tenant_type.rb +++ /dev/null @@ -1,17 +0,0 @@ -Types::TenantType = GraphQL::ObjectType.define do - name 'Tenant' - description 'Tenants segregate and nest users and content' - - field :id, !types.ID - field :name, !types.String - field :name_id, !types.String - field :description, !types.String - field :parent, types[Types::TenantType] - field :children, types[Types::TenantType] - field :owner, !types[Types::UserType] - field :created_at, !Types::DateTimeType - field :updated_at, !Types::DateTimeType - field :deleted_at, Types::DateTimeType - - # TODO: Owned ContentItems, etc -end diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb deleted file mode 100644 index d92e74316..000000000 --- a/app/graphql/types/user_type.rb +++ /dev/null @@ -1,18 +0,0 @@ -Types::UserType = GraphQL::ObjectType.define do - name 'User' - description 'Cortex administrator or content creator' - - field :id, !types.ID - field :firstname, !types.String - field :lastname, !types.String - field :locale, !types.String - field :timezone, !types.String - field :created_at, !Types::DateTimeType - field :updated_at, !Types::DateTimeType - field :deleted_at, Types::DateTimeType - - field :tenants, !types[Types::TenantType] - field :active_tenant, !types[Types::TenantType] - - # TODO: Owned ContentItems, etc -end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb deleted file mode 100644 index 4df973b9b..000000000 --- a/app/helpers/application_helper.rb +++ /dev/null @@ -1,71 +0,0 @@ -module ApplicationHelper - def title - title = 'Cortex Administration' - title += " | #{render_breadcrumbs builder: TitleBuilder}" if render_breadcrumbs - title - end - - def extra_config - Cortex.config.extra - end - - def user_session_props - { - environment: environment, - active_tenant: current_user.active_tenant, - current_user: current_user, - tenants: current_user.tenants_with_children, - csrf_token: form_authenticity_token, - sidebarExpanded: (current_page? root_path), - environment_abbreviated: environment_abbreviated - } - end - - def wizard_props - if @wizard - @wizard.data.merge({ - content_type: @content_type, - content_item: @content_item, - fields: @content_type.fields - }) - else - {} - end - end - - def index_props - @index || {} - end - - def cortex_props - user_session_props.merge({ - wizard: wizard_props, - index: index_props - }) - end - - def qualtrics_domain - extra_config.qualtrics_id.delete('_').downcase - end - - def flag_enabled?(flag_name) - Flipper.enabled?(flag_name, current_user, request) - end - - def environment - request.local? ? :local : Rails.env - end - - def environment_abbreviated - case environment - when 'production' - :prd - when 'staging' - :stg - when 'development' - :dev - else - :loc - end - end -end diff --git a/app/helpers/cells/association_helper.rb b/app/helpers/cells/association_helper.rb deleted file mode 100644 index 8c84d726f..000000000 --- a/app/helpers/cells/association_helper.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Cells - module AssociationHelper - def associated_content_item - @options[:associated_content_item] - end - - def associated_primary_field - @options[:associated_primary_field] - end - - def associated_primary_field_type_class - @options[:associated_primary_field_type_class] - end - - def associated_primary_field_item - @options[:associated_primary_field_item] - end - - def associated_content_item_title - @options[:associated_content_item_title] - end - end -end diff --git a/app/helpers/content_item_helper.rb b/app/helpers/content_item_helper.rb deleted file mode 100644 index ce219a72e..000000000 --- a/app/helpers/content_item_helper.rb +++ /dev/null @@ -1,88 +0,0 @@ -module ContentItemHelper - def content_type - @content_type ||= ContentType.find_by_id(params[:content_type_id]) - end - - def content_item - @content_item ||= ContentItemService.new(id: params[:id], content_item_params: content_item_params, current_user: current_user, state: params[:content_item][:state]) - end - - def content_item_reload(content_item) - @content_item = content_item - content_type.fields.each do |field| - @content_item.field_items << FieldItem.new(field: field, data: field_item_param_data(params_lookup[field.id])) - end - @content_item - end - - def content_item_params - params.require(:content_item).permit( - :creator_id, - :content_type_id, - field_items_attributes: field_items_attributes_params, - ) - end - - def field_items_attributes_params - field_items_attributes_as_array = params['content_item']['field_items_attributes'].values - - permitted_keys = {} - field_items_attributes_as_array.each { |hash| hash.each_key { |key| permitted_keys[key.to_s] = [] } } - - permit_attribute_params(field_items_attributes_as_array, permitted_keys) - end - - def permit_attribute_params(param_array, permitted_keys) - param_array.each do |param_hash| - permitted_keys.keys.each do |key| - if param_hash[key].is_a?(Hash) - permitted_keys[key] << permit_param(param_hash[key]) - end - permitted_keys[key].flatten! - end - end - - sanitize_parameters(permitted_keys) - end - - def permit_param(param) - if param.values[0].is_a?(Hash) - { param.keys[0].to_sym => param.values[0].keys } - else - param.keys - end - end - - def sanitize_parameters(permitted_keys) - permitted_keys.map do |key, value| - if value.empty? - key - else - { key => value.uniq } - end - end - end - - def index_parameters - @index_parameters = {} - params['content_item']['field_items_attributes'].each do |param| - @index_parameters[params['content_item']['field_items_attributes'][param]['field_id']] = params['content_item']['field_items_attributes'][param] - end - @index_parameters - end - - def params_lookup - @params_lookup ||= index_parameters - end - - def field_item_param_data(field_item_params) - return {} unless field_item_params - params_hash = field_item_params.to_unsafe_h - params_hash['data'] || {} - end - - def validation_message(base_message) - msg_array = base_message.gsub('Validation failed:', '').gsub('Field items', '').split(',') - msg_array.map { |message| message.strip.titleize } - end -end diff --git a/app/helpers/cortex/application_helper.rb b/app/helpers/cortex/application_helper.rb new file mode 100644 index 000000000..1f1ae4d62 --- /dev/null +++ b/app/helpers/cortex/application_helper.rb @@ -0,0 +1,75 @@ +require 'breadcrumbs/title_builder' + +module Cortex + module ApplicationHelper + def title + title = 'Cortex Administration' + title += " | #{render_breadcrumbs builder: TitleBuilder}" if render_breadcrumbs + title + end + + def extra_config + Cortex.config[:extra] + end + + def user_session_props + { + environment: environment, + active_tenant: current_user.active_tenant, + current_user: current_user, + tenants: current_user.tenants_with_children, + csrf_token: form_authenticity_token, + sidebarExpanded: (current_page? cortex.root_path), + environment_abbreviated: environment_abbreviated + } + end + + def wizard_props + if @wizard + @wizard.data.merge({ + content_type: @content_type, + content_item: @content_item, + fields: @content_type.fields + }) + else + {} + end + end + + def index_props + @index || {} + end + + def cortex_props + user_session_props.merge({ + wizard: wizard_props, + index: index_props + }) + end + + def qualtrics_domain + extra_config[:qualtrics_id].delete('_').downcase + end + + def flag_enabled?(flag_name) + Flipper.enabled?(flag_name, current_user, request) + end + + def environment + request.local? ? :local : Rails.env + end + + def environment_abbreviated + case environment + when 'production' + :prd + when 'staging' + :stg + when 'development' + :dev + else + :loc + end + end + end +end diff --git a/app/helpers/cortex/cells/association_helper.rb b/app/helpers/cortex/cells/association_helper.rb new file mode 100644 index 000000000..62940b6d7 --- /dev/null +++ b/app/helpers/cortex/cells/association_helper.rb @@ -0,0 +1,25 @@ +module Cortex + module Cells + module AssociationHelper + def associated_content_item + @options[:associated_content_item] + end + + def associated_primary_field + @options[:associated_primary_field] + end + + def associated_primary_field_type_class + @options[:associated_primary_field_type_class] + end + + def associated_primary_field_item + @options[:associated_primary_field_item] + end + + def associated_content_item_title + @options[:associated_content_item_title] + end + end + end +end diff --git a/app/helpers/cortex/content_item_helper.rb b/app/helpers/cortex/content_item_helper.rb new file mode 100644 index 000000000..63753819f --- /dev/null +++ b/app/helpers/cortex/content_item_helper.rb @@ -0,0 +1,90 @@ +module Cortex + module ContentItemHelper + def content_type + @content_type ||= Cortex::ContentType.find_by_id(params[:content_type_id]) + end + + def content_item + @content_item ||= Cortex::ContentItemService.new(id: params[:id], content_item_params: content_item_params, current_user: current_user, state: params[:content_item][:state]) + end + + def content_item_reload(content_item) + @content_item = content_item + content_type.fields.each do |field| + @content_item.field_items << Cortex::FieldItem.new(field: field, data: field_item_param_data(params_lookup[field.id])) + end + @content_item + end + + def content_item_params + params.require(:content_item).permit( + :creator_id, + :content_type_id, + field_items_attributes: field_items_attributes_params, + ) + end + + def field_items_attributes_params + field_items_attributes_as_array = params['content_item']['field_items_attributes'].values + + permitted_keys = {} + field_items_attributes_as_array.each { |hash| hash.each_key { |key| permitted_keys[key.to_s] = [] } } + + permit_attribute_params(field_items_attributes_as_array, permitted_keys) + end + + def permit_attribute_params(param_array, permitted_keys) + param_array.each do |param_hash| + permitted_keys.keys.each do |key| + if param_hash[key].is_a?(Hash) + permitted_keys[key] << permit_param(param_hash[key]) + end + permitted_keys[key].flatten! + end + end + + sanitize_parameters(permitted_keys) + end + + def permit_param(param) + if param.values[0].is_a?(Hash) + { param.keys[0].to_sym => param.values[0].keys } + else + param.keys + end + end + + def sanitize_parameters(permitted_keys) + permitted_keys.map do |key, value| + if value.empty? + key + else + { key => value.uniq } + end + end + end + + def index_parameters + @index_parameters = {} + params['content_item']['field_items_attributes'].each do |param| + @index_parameters[params['content_item']['field_items_attributes'][param]['field_id']] = params['content_item']['field_items_attributes'][param] + end + @index_parameters + end + + def params_lookup + @params_lookup ||= index_parameters + end + + def field_item_param_data(field_item_params) + return {} unless field_item_params + params_hash = field_item_params.to_unsafe_h + params_hash['data'] || {} + end + + def validation_message(base_message) + msg_array = base_message.gsub('Validation failed:', '').gsub('Field items', '').split(',') + msg_array.map { |message| message.strip.titleize } + end + end +end diff --git a/app/helpers/cortex/dashboard_helper.rb b/app/helpers/cortex/dashboard_helper.rb new file mode 100644 index 000000000..af2d4fed6 --- /dev/null +++ b/app/helpers/cortex/dashboard_helper.rb @@ -0,0 +1,19 @@ +module Cortex + module DashboardHelper + def news_feed + [] # need to re-implement this feature using Beta Cortex blogposts + end + + def news_feed_tenant + @news_feed_tenant ||= Tenant.find_by_id(Cortex.config[:cortex][:news_feed][:tenant]) + end + + def media_content_type + @media_content_type ||= current_user.active_tenant.search_up_organization_for(Cortex::ContentType, :name, 'Media').first + end + + def employer_blog_content_type + @employer_blog_content_type ||= current_user.active_tenant.search_up_organization_for(Cortex::ContentType, :name, 'Employer Blog').first + end + end +end diff --git a/app/helpers/cortex/popup_helper.rb b/app/helpers/cortex/popup_helper.rb new file mode 100644 index 000000000..a4cdd22bc --- /dev/null +++ b/app/helpers/cortex/popup_helper.rb @@ -0,0 +1,27 @@ +module Cortex + module PopupHelper + # TODO: This needs to be in a plugin + + def media_content_type + @media_content_type ||= current_user.active_tenant.search_up_organization_for(Cortex::ContentType, :name, 'Media').first + end + + def media_content_items + @media_content_items ||= media_content_type.content_items + end + + def media_asset_field + @media_asset_field ||= media_content_type.fields.find_by_name('Asset') + end + + def media_image_content_items + @media_image_content_items ||= media_content_items.select do |content_item| + MimeMagic.new(content_item.field_items.find_by_field_id(media_asset_field).data['asset']['versions']['original']['mime_type']).mediatype == 'image' + end + end + + def media_index + @media_index ||= IndexDecoratorService.new(content_type: media_content_type) + end + end +end diff --git a/app/helpers/cortex/rss_helper.rb b/app/helpers/cortex/rss_helper.rb new file mode 100644 index 000000000..9eb9d1a0d --- /dev/null +++ b/app/helpers/cortex/rss_helper.rb @@ -0,0 +1,78 @@ +module Cortex + module RssHelper + include ActiveSupport::Inflector + + def rss_content_type + @content_type = Cortex::ContentType.find_by_name(titleize(params[:content_type_name])) || Cortex::ContentType.new + end + + def rss_decorator + @rss_decorator ||= rss_content_type.rss_decorator.data + end + + def channel_spec + @channel_spec ||= CortexRssSpec::Channel.feed + end + + def item_spec + @item_spec ||= CortexRssSpec::Item.feed + end + + def key_name(key) + key.split(":").first + end + + def tag_data(tag_data_hash, rss_content_item) + if tag_data_hash.keys.include?("string") + tag_data_hash["string"] + elsif tag_data_hash.keys.include?("field") + field_item_data(tag_data_hash["field"], rss_content_item, tag_data_hash) + elsif tag_data_hash.keys.include?("method") + method_data(tag_data_hash["method"], rss_content_item) + elsif tag_data_hash.keys.include?("transaction") + transaction_data(tag_data_hash["transaction"], rss_content_item) + elsif tag_data_hash.keys.include?("media") + media_data(tag_data_hash["media"], rss_content_item) + end + end + + private + + def field_item_data(field_id, rss_content_item, tag_data_hash) + values = rss_content_item.field_items.find_by_field_id(field_id).data.values + tag_data_hash.has_key?("multiple") ? (values.join(tag_data_hash["multiple"])) : (values.join) + end + + def method_data(method_hash, rss_content_item) + method_hash['name'].split('.').inject(rss_content_item, :send) + rss_content_item.send(method_hash["name"], *method_hash["args"]) + end + + def transaction_data(config_hash, rss_content_item) + transaction_klass = "#{config_hash['name']}_transaction".classify.safe_constantize + transaction_klass.new&.call(content_item: rss_content_item, args: config_hash['args'])&.value + end + + def media_data(media_hash, rss_content_item) + linked_field_item = rss_content_item.field_items.find_by_field_id(media_hash["field"]) + + field_name = linked_field_item.field.metadata["field_name"] + asset_content_item_id = linked_field_item.data["content_item_id"] + field_item = Field.find_by_name(field_name).field_items.find { |field_item| field_item.content_item_id == asset_content_item_id } + + if field_item.nil? + {} + else + asset_data = field_item.data['asset']['versions']['rss'] + + { + "url": asset_data["url"], + "type": asset_data["mime_type"], + "medium": media_hash["medium"], + "width": media_hash["width"] || asset_data["dimensions"]["width"], + "height": media_hash["height"] || asset_data["dimensions"]["height"] + } + end + end + end +end diff --git a/app/helpers/cortex/widget_parsers/media_helper.rb b/app/helpers/cortex/widget_parsers/media_helper.rb new file mode 100644 index 000000000..deddcb1be --- /dev/null +++ b/app/helpers/cortex/widget_parsers/media_helper.rb @@ -0,0 +1,53 @@ +module Cortex + module WidgetParsers + module MediaHelper + # This needs to be abstracted to a plugin + + def self.parse(body) + body_document = document_for body + + widget_nodes_for(body_document).each do |widget_node| + widget_node.inner_html = render_widget_inner widget_node + end + + body_document.to_html + end + + def self.document_for(html) + Nokogiri::HTML::DocumentFragment.parse html + end + + def self.widget_nodes_for(document) + document.css 'media' + end + + def self.render_widget_inner(widget) + Nokogiri::HTML::Builder.new do |doc| + element, tag_type = content_item_element(widget['id']) + element.merge!({ width: widget['width'], height: widget['height'], alt: widget['alt'], style: widget['style'], class: widget['class'] }) + + doc.send(tag_type, element) + end.doc.root + end + + def self.content_item_element(id) + asset_field_item = ContentItem.find(id).field_items.find { |field_item| field_item.field.field_type_instance.is_a?(AssetFieldType) } + url = asset_field_item.data['asset']['versions']['original']['url'] + + if image? asset_field_item.data['asset']['versions']['original']['mime_type'] + element = { src: url } + tag_type = 'img' + else + element = { href: url } + tag_type = 'a' + end + + [element, tag_type] + end + + def self.image?(mime_type) + MimeMagic.new(mime_type).mediatype == 'image' + end + end + end +end diff --git a/app/helpers/cortex/widget_parsers_helper.rb b/app/helpers/cortex/widget_parsers_helper.rb new file mode 100644 index 000000000..9ada24521 --- /dev/null +++ b/app/helpers/cortex/widget_parsers_helper.rb @@ -0,0 +1,17 @@ +module Cortex + module WidgetParsersHelper + def parse_widgets!(field_item) + Cortex.available_tag_parsers.each do |tag_parser| + # TODO: Alternatively, we could decide to dynamically iterate through all + # modules inside the WidgetParsers namespace. This way feels brittle. + parser_module = "Cortex::WidgetParsers::#{tag_parser.capitalize}Helper".constantize + field_item.data['text'] = parser_module.parse(field_item.data['text']) + end + end + + def widget_parsers_as_json + # TODO: iterate through, provide JSON of Widget parser for frontend + {}.to_json + end + end +end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb deleted file mode 100644 index d26b2fcde..000000000 --- a/app/helpers/dashboard_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -module DashboardHelper - def news_feed - [] # need to re-implement this feature using Beta Cortex blogposts - end - - def news_feed_tenant - @news_feed_tenant ||= Tenant.find_by_id(Cortex.config.cortex.news_feed.tenant) - end - - def media_content_type - @media_content_type ||= current_user.active_tenant.search_up_organization_for(ContentType, :name, 'Media').first - end - - def employer_blog_content_type - @employer_blog_content_type ||= current_user.active_tenant.search_up_organization_for(ContentType, :name, 'Employer Blog').first - end -end diff --git a/app/helpers/popup_helper.rb b/app/helpers/popup_helper.rb deleted file mode 100644 index 03f7127f7..000000000 --- a/app/helpers/popup_helper.rb +++ /dev/null @@ -1,25 +0,0 @@ -module PopupHelper - # TODO: This needs to be in a plugin - - def media_content_type - @media_content_type ||= current_user.active_tenant.search_up_organization_for(ContentType, :name, 'Media').first - end - - def media_content_items - @media_content_items ||= media_content_type.content_items - end - - def media_asset_field - @media_asset_field ||= media_content_type.fields.find_by_name('Asset') - end - - def media_image_content_items - @media_image_content_items ||= media_content_items.select do |content_item| - MimeMagic.new(content_item.field_items.find_by_field_id(media_asset_field).data['asset']['versions']['original']['mime_type']).mediatype == 'image' - end - end - - def media_index - @media_index ||= IndexDecoratorService.new(content_type: media_content_type) - end -end diff --git a/app/helpers/rss_helper.rb b/app/helpers/rss_helper.rb deleted file mode 100644 index 8dee956b2..000000000 --- a/app/helpers/rss_helper.rb +++ /dev/null @@ -1,76 +0,0 @@ -module RssHelper - include ActiveSupport::Inflector - - def rss_content_type - @content_type = ContentType.find_by_name(titleize(params[:content_type_name])) || ContentType.new - end - - def rss_decorator - @rss_decorator ||= rss_content_type.rss_decorator.data - end - - def channel_spec - @channel_spec ||= CortexRssSpec::Channel.feed - end - - def item_spec - @item_spec ||= CortexRssSpec::Item.feed - end - - def key_name(key) - key.split(":").first - end - - def tag_data(tag_data_hash, rss_content_item) - if tag_data_hash.keys.include?("string") - tag_data_hash["string"] - elsif tag_data_hash.keys.include?("field") - field_item_data(tag_data_hash["field"], rss_content_item, tag_data_hash) - elsif tag_data_hash.keys.include?("method") - method_data(tag_data_hash["method"], rss_content_item) - elsif tag_data_hash.keys.include?("transaction") - transaction_data(tag_data_hash["transaction"], rss_content_item) - elsif tag_data_hash.keys.include?("media") - media_data(tag_data_hash["media"], rss_content_item) - end - end - - private - - def field_item_data(field_id, rss_content_item, tag_data_hash) - values = rss_content_item.field_items.find_by_field_id(field_id).data.values - tag_data_hash.has_key?("multiple") ? (values.join(tag_data_hash["multiple"])) : (values.join) - end - - def method_data(method_hash, rss_content_item) - method_hash['name'].split('.').inject(rss_content_item, :send) - rss_content_item.send(method_hash["name"], *method_hash["args"]) - end - - def transaction_data(config_hash, rss_content_item) - transaction_klass = "#{config_hash['name']}_transaction".classify.safe_constantize - transaction_klass.new&.call(content_item: rss_content_item, args: config_hash['args'])&.value - end - - def media_data(media_hash, rss_content_item) - linked_field_item = rss_content_item.field_items.find_by_field_id(media_hash["field"]) - - field_name = linked_field_item.field.metadata["field_name"] - asset_content_item_id = linked_field_item.data["content_item_id"] - field_item = Field.find_by_name(field_name).field_items.find { |field_item| field_item.content_item_id == asset_content_item_id } - - if field_item.nil? - {} - else - asset_data = field_item.data['asset']['versions']['rss'] - - { - "url": asset_data["url"], - "type": asset_data["mime_type"], - "medium": media_hash["medium"], - "width": media_hash["width"] || asset_data["dimensions"]["width"], - "height": media_hash["height"] || asset_data["dimensions"]["height"] - } - end - end -end diff --git a/app/helpers/widget_parsers/media_helper.rb b/app/helpers/widget_parsers/media_helper.rb deleted file mode 100644 index 578353e65..000000000 --- a/app/helpers/widget_parsers/media_helper.rb +++ /dev/null @@ -1,51 +0,0 @@ -module WidgetParsers - module MediaHelper - # This needs to be abstracted to a plugin - - def self.parse(body) - body_document = document_for body - - widget_nodes_for(body_document).each do |widget_node| - widget_node.inner_html = render_widget_inner widget_node - end - - body_document.to_html - end - - def self.document_for(html) - Nokogiri::HTML::DocumentFragment.parse html - end - - def self.widget_nodes_for(document) - document.css 'media' - end - - def self.render_widget_inner(widget) - Nokogiri::HTML::Builder.new do |doc| - element, tag_type = content_item_element(widget['id']) - element.merge!({width: widget['width'], height: widget['height'], alt: widget['alt'], style: widget['style'], class: widget['class']}) - - doc.send(tag_type, element) - end.doc.root - end - - def self.content_item_element(id) - asset_field_item = ContentItem.find(id).field_items.find { |field_item| field_item.field.field_type_instance.is_a?(AssetFieldType) } - url = asset_field_item.data['asset']['versions']['original']['url'] - - if image? asset_field_item.data['asset']['versions']['original']['mime_type'] - element = { src: url } - tag_type = 'img' - else - element = { href: url } - tag_type = 'a' - end - - [element, tag_type] - end - - def self.image?(mime_type) - MimeMagic.new(mime_type).mediatype == 'image' - end - end -end diff --git a/app/helpers/widget_parsers_helper.rb b/app/helpers/widget_parsers_helper.rb deleted file mode 100644 index ab60f170e..000000000 --- a/app/helpers/widget_parsers_helper.rb +++ /dev/null @@ -1,15 +0,0 @@ -module WidgetParsersHelper - def parse_widgets!(field_item) - Cortex.available_tag_parsers.each do |tag_parser| - # TODO: Alternatively, we could decide to dynamically iterate through all - # modules inside the WidgetParsers namespace. This way feels brittle. - parser_module = "WidgetParsers::#{tag_parser.capitalize}Helper".constantize - field_item.data['text'] = parser_module.parse(field_item.data['text']) - end - end - - def widget_parsers_as_json - # TODO: iterate through, provide JSON of Widget parser for frontend - {}.to_json - end -end diff --git a/app/javascript/bundles/cortex/components/wizard/step.jsx b/app/javascript/bundles/cortex/components/wizard/step.jsx deleted file mode 100644 index d501423c7..000000000 --- a/app/javascript/bundles/cortex/components/wizard/step.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import Column from './column' - -class Step extends React.PureComponent { - renderColumn = (column, index) => { - return - } - render() { - const { name, heading, contentItemFieldLookup, description, columns } = this.props - - return ( -
- { columns.map(this.renderColumn) } -
- ) - } -} - -export default Step diff --git a/app/javascript/bundles/cortex/helpers/tenant_lookup.jsx b/app/javascript/bundles/cortex/helpers/tenant_lookup.jsx deleted file mode 100644 index 2ba60cc3b..000000000 --- a/app/javascript/bundles/cortex/helpers/tenant_lookup.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import { NOT_DEFINED } from '../constants/type_constants' - -const TenantIndex = (tenants) => tenants.reduce((lookup, tenant, i) => { - lookup[tenant.id] = tenant - return lookup; -}, {topLevel: []}) - -const TenantLookup = (tenants) => { - - return tenants.reduce((lookup, tenant, i) => { - if(tenant.parent_id === null) { - lookup.topLevel.push(tenant.id); - return lookup; - } - if(lookup[tenant.parent_id].children === NOT_DEFINED) { - lookup[tenant.parent_id].children = [] - } - lookup[tenant.parent_id].children.push(tenant.id) - return lookup; - }, TenantIndex(tenants)) -} - -export default TenantLookup diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js deleted file mode 100644 index 81b5ba345..000000000 --- a/app/javascript/packs/application.js +++ /dev/null @@ -1,8 +0,0 @@ -/* eslint no-console:0 */ -// This file is automatically compiled by Webpack, along with any other files -// present in this directory. You're encouraged to place your actual application logic in -// a relevant structure within app/javascript and only use these pack files to reference -// that code so it'll be compiled. -// -// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate -// layout file, like app/views/layouts/application.html.erb diff --git a/app/javascript/packs/cortex-bundle.jsx b/app/javascript/packs/cortex-bundle.jsx deleted file mode 100644 index 86e5fe4aa..000000000 --- a/app/javascript/packs/cortex-bundle.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import ReactOnRails from 'react-on-rails'; -import Layout from '../bundles/cortex/containers/layout'; -import { Provider } from 'react-redux'; -import CortexStore from '../bundles/cortex/store/store'; - -ReactOnRails.registerStore({ - CortexStore -}); - -const CortexApp = (props, railsContext) => { - const store = ReactOnRails.getStore('CortexStore'); - return ( - - - - ); -}; - -ReactOnRails.register({ CortexApp }); diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb deleted file mode 100644 index a009ace51..000000000 --- a/app/jobs/application_job.rb +++ /dev/null @@ -1,2 +0,0 @@ -class ApplicationJob < ActiveJob::Base -end diff --git a/app/jobs/cortex/application_job.rb b/app/jobs/cortex/application_job.rb new file mode 100644 index 000000000..0fa53320b --- /dev/null +++ b/app/jobs/cortex/application_job.rb @@ -0,0 +1,4 @@ +module Cortex + class ApplicationJob < ActiveJob::Base + end +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb deleted file mode 100644 index 2949dc317..000000000 --- a/app/mailers/application_mailer.rb +++ /dev/null @@ -1,4 +0,0 @@ -class ApplicationMailer < ActionMailer::Base - default from: 'Cortex CMS ' - layout 'mailer' -end diff --git a/app/mailers/cortex/application_mailer.rb b/app/mailers/cortex/application_mailer.rb new file mode 100644 index 000000000..49d94c792 --- /dev/null +++ b/app/mailers/cortex/application_mailer.rb @@ -0,0 +1,6 @@ +module Cortex + class ApplicationMailer < ActionMailer::Base + default from: 'Cortex CMS ' + layout 'mailer' + end +end diff --git a/app/mailers/cortex/password_reset_mailer.rb b/app/mailers/cortex/password_reset_mailer.rb new file mode 100644 index 000000000..989ac38ec --- /dev/null +++ b/app/mailers/cortex/password_reset_mailer.rb @@ -0,0 +1,13 @@ +module Cortex + class PasswordResetMailer < Cortex::ApplicationMailer + # Subject can be set in your I18n file at config/locales/en.yml + # with the following lookup: + # + # en.password_reset_mailer.send_password_reset.subject + # + def send_password_reset(params = {}) + @password = params[:password] + mail to: params[:email], subject: "Cortex CMS Password Reset" + end + end +end diff --git a/app/mailers/password_reset_mailer.rb b/app/mailers/password_reset_mailer.rb deleted file mode 100644 index e73e826b7..000000000 --- a/app/mailers/password_reset_mailer.rb +++ /dev/null @@ -1,11 +0,0 @@ -class PasswordResetMailer < ApplicationMailer - # Subject can be set in your I18n file at config/locales/en.yml - # with the following lookup: - # - # en.password_reset_mailer.send_password_reset.subject - # - def send_password_reset(params = {}) - @password = params[:password] - mail to: params[:email], subject: "CB Cortex Password Reset" - end -end diff --git a/app/models/application_record.rb b/app/models/application_record.rb deleted file mode 100644 index 04c3bd1c4..000000000 --- a/app/models/application_record.rb +++ /dev/null @@ -1,15 +0,0 @@ -class ApplicationRecord < ActiveRecord::Base - self.abstract_class = true - - acts_as_paranoid - - # A bug in ES-Rails 5.x is breaking document updates: - # https://github.com/elastic/elasticsearch-rails/issues/669 - after_commit :refresh_elasticsearch_index, on: :update - - private - - def refresh_elasticsearch_index - __elasticsearch__.index_document(refresh: true) if defined? __elasticsearch__ - end -end diff --git a/app/models/concerns/belongs_to_tenant.rb b/app/models/concerns/belongs_to_tenant.rb deleted file mode 100644 index 820900ab8..000000000 --- a/app/models/concerns/belongs_to_tenant.rb +++ /dev/null @@ -1,11 +0,0 @@ -module BelongsToTenant - extend ActiveSupport::Concern - - included do - belongs_to :tenant - - validates :tenant, presence: true - - scope :find_by_tenant, ->(tenant) { where(tenant_id: tenant.id) } - end -end diff --git a/app/models/concerns/cortex/belongs_to_tenant.rb b/app/models/concerns/cortex/belongs_to_tenant.rb new file mode 100644 index 000000000..0aaa528de --- /dev/null +++ b/app/models/concerns/cortex/belongs_to_tenant.rb @@ -0,0 +1,13 @@ +module Cortex + module BelongsToTenant + extend ActiveSupport::Concern + + included do + belongs_to :tenant + + validates :tenant, presence: true + + scope :find_by_tenant, ->(tenant) { where(tenant_id: tenant.id) } + end + end +end diff --git a/app/models/concerns/cortex_rss_spec/channel.rb b/app/models/concerns/cortex/cortex_rss_spec/channel.rb similarity index 63% rename from app/models/concerns/cortex_rss_spec/channel.rb rename to app/models/concerns/cortex/cortex_rss_spec/channel.rb index 498253d0b..ab6e59e91 100644 --- a/app/models/concerns/cortex_rss_spec/channel.rb +++ b/app/models/concerns/cortex/cortex_rss_spec/channel.rb @@ -1,10 +1,12 @@ -module CortexRssSpec - module Channel - def self.feed - %w( +module Cortex + module CortexRssSpec + module Channel + def self.feed + %w( title link description language copyright managingEditor webMaster pubDate lastBuildDate category docs cloud ttl rating textInput ) + end end end end diff --git a/app/models/concerns/cortex_rss_spec/item.rb b/app/models/concerns/cortex/cortex_rss_spec/item.rb similarity index 57% rename from app/models/concerns/cortex_rss_spec/item.rb rename to app/models/concerns/cortex/cortex_rss_spec/item.rb index 6ce6a42e0..5edff80d1 100644 --- a/app/models/concerns/cortex_rss_spec/item.rb +++ b/app/models/concerns/cortex/cortex_rss_spec/item.rb @@ -1,9 +1,11 @@ -module CortexRssSpec - module Item - def self.feed - %w( +module Cortex + module CortexRssSpec + module Item + def self.feed + %w( title link description author category comments enclosure guid pubDate source media:content ) + end end end end diff --git a/app/models/concerns/cortex/has_firstname_lastname.rb b/app/models/concerns/cortex/has_firstname_lastname.rb new file mode 100644 index 000000000..c7327f395 --- /dev/null +++ b/app/models/concerns/cortex/has_firstname_lastname.rb @@ -0,0 +1,11 @@ +module Cortex + module HasFirstnameLastname + extend ActiveSupport::Concern + + included do + def fullname + lastname.to_s == '' ? firstname : "#{firstname} #{lastname}" + end + end + end +end diff --git a/app/models/concerns/cortex/has_gravatar.rb b/app/models/concerns/cortex/has_gravatar.rb new file mode 100644 index 000000000..f445905f9 --- /dev/null +++ b/app/models/concerns/cortex/has_gravatar.rb @@ -0,0 +1,18 @@ +module Cortex + module HasGravatar + extend ActiveSupport::Concern + + included do + def avatars + hash = email ? Digest::MD5.hexdigest(email) : '00000000000000000000000000000000' + url = "//www.gravatar.com/avatar/#{hash}" + { + default: "#{url}", + small: "#{url}?s=200", + medium: "#{url}?s=400", + large: "#{url}?s=1000" + } + end + end + end +end diff --git a/app/models/concerns/cortex/searchable.rb b/app/models/concerns/cortex/searchable.rb new file mode 100644 index 000000000..cf3e8c31f --- /dev/null +++ b/app/models/concerns/cortex/searchable.rb @@ -0,0 +1,11 @@ +module Cortex + module Searchable + extend ActiveSupport::Concern + + included do + include Elasticsearch::Model + + index_name "#{Rails.env}_#{model_name.to_s.parameterize(separator: '_')}" + end + end +end diff --git a/app/models/concerns/cortex/searchable_content_item.rb b/app/models/concerns/cortex/searchable_content_item.rb new file mode 100644 index 000000000..8156493c1 --- /dev/null +++ b/app/models/concerns/cortex/searchable_content_item.rb @@ -0,0 +1,31 @@ +module Cortex + module SearchableContentItem + extend ActiveSupport::Concern + + included do + include Cortex::Searchable + + after_commit on: [:create] do + __elasticsearch__.index_document(index: content_type.content_items_index_name) + end + + after_commit on: [:update] do + # Perform a full document re-index, as update_document ignores methods: https://github.com/elastic/elasticsearch-rails/issues/306#issuecomment-121817954 + __elasticsearch__.index_document(index: content_type.content_items_index_name) + end + + after_commit on: [:destroy] do + __elasticsearch__.delete_document(index: content_type.content_items_index_name) + end + + def as_indexed_json(options = {}) + json = as_json + field_items.each do |field_item| + field_type = field_item.field.field_type_instance(field_name: field_item.field.name) + json.merge!(field_type.field_item_as_indexed_json_for_field_type(field_item)) + end + json + end + end + end +end diff --git a/app/models/concerns/cortex/searchable_content_item_for_content_type.rb b/app/models/concerns/cortex/searchable_content_item_for_content_type.rb new file mode 100644 index 000000000..08e84ed8c --- /dev/null +++ b/app/models/concerns/cortex/searchable_content_item_for_content_type.rb @@ -0,0 +1,52 @@ +module Cortex + module SearchableContentItemForContentType + extend ActiveSupport::Concern + + included do + after_commit on: [:create] do + self.class.__elasticsearch__.create_index!(index: content_items_index_name, mappings: content_item_mappings) + end + + after_commit on: [:destroy] do + self.class.__elasticsearch__.delete_index!(index: content_items_index_name) + end + + after_commit on: [:update] do + self.class.__elasticsearch__.delete_index!(index: content_items_index_name) + self.class.__elasticsearch__.create_index!(index: content_items_index_name, mappings: content_item_mappings) + # TODO: implement & move import to ContentItem + #self.class.__elasticsearch__.import() + end + + def content_items_index_name + @content_items_index_name ||= "#{Rails.env}_content_type_#{name_id}_content_items" + end + end + + private + + def content_item_mappings + mappings = Elasticsearch::Model::Indexing::Mappings.new(content_items_index_name, { dynamic: false }) + mappings.indexes :tenant_id, type: :keyword, index: :not_analyzed + mappings.indexes :content_type_id, type: :keyword, index: :not_analyzed + mappings.indexes :creator_id, type: :keyword, index: :not_analyzed + mappings.indexes :updated_by_id, type: :keyword, index: :not_analyzed + mappings.indexes :state, analyzer: :keyword, index: :not_analyzed + mappings.indexes :created_at, type: :date, include_in_all: false + mappings.indexes :updated_at, type: :date, include_in_all: false + mappings.indexes :deleted_at, type: :date, include_in_all: false + + fields.each do |field| + mappings.indexes field.elasticsearch_mapping[:name], content_item_field_mappings(field) + end + + mappings + end + + def content_item_field_mappings(field) + mappings = { type: field.elasticsearch_mapping[:type] } + mappings[:analyzer] = field.elasticsearch_mapping[:analyzer] if field.elasticsearch_mapping[:analyzer] + mappings + end + end +end diff --git a/app/models/concerns/cortex/searchable_content_type.rb b/app/models/concerns/cortex/searchable_content_type.rb new file mode 100644 index 000000000..d039a9140 --- /dev/null +++ b/app/models/concerns/cortex/searchable_content_type.rb @@ -0,0 +1,11 @@ +module Cortex + module SearchableContentType + extend ActiveSupport::Concern + + included do + include Cortex::Searchable + + # TODO: ContentType mappings + end + end +end diff --git a/app/models/concerns/has_firstname_lastname.rb b/app/models/concerns/has_firstname_lastname.rb deleted file mode 100644 index 5bb387fad..000000000 --- a/app/models/concerns/has_firstname_lastname.rb +++ /dev/null @@ -1,9 +0,0 @@ -module HasFirstnameLastname - extend ActiveSupport::Concern - - included do - def fullname - lastname.to_s == '' ? firstname : "#{firstname} #{lastname}" - end - end -end diff --git a/app/models/concerns/has_gravatar.rb b/app/models/concerns/has_gravatar.rb deleted file mode 100644 index 562b73590..000000000 --- a/app/models/concerns/has_gravatar.rb +++ /dev/null @@ -1,16 +0,0 @@ -module HasGravatar - extend ActiveSupport::Concern - - included do - def avatars - hash = email ? Digest::MD5.hexdigest(email) : '00000000000000000000000000000000' - url = "//www.gravatar.com/avatar/#{hash}" - { - default: "#{url}", - small: "#{url}?s=200", - medium: "#{url}?s=400", - large: "#{url}?s=1000" - } - end - end -end diff --git a/app/models/concerns/searchable.rb b/app/models/concerns/searchable.rb deleted file mode 100644 index e70babf43..000000000 --- a/app/models/concerns/searchable.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Searchable - extend ActiveSupport::Concern - - included do - include Elasticsearch::Model - - index_name "#{Rails.env}_#{model_name.to_s.parameterize(separator: '_')}" - end -end diff --git a/app/models/concerns/searchable_content_item.rb b/app/models/concerns/searchable_content_item.rb deleted file mode 100644 index a9377f39e..000000000 --- a/app/models/concerns/searchable_content_item.rb +++ /dev/null @@ -1,29 +0,0 @@ -module SearchableContentItem - extend ActiveSupport::Concern - - included do - include Searchable - - after_commit on: [:create] do - __elasticsearch__.index_document(index: content_type.content_items_index_name) - end - - after_commit on: [:update] do - # Perform a full document re-index, as update_document ignores methods: https://github.com/elastic/elasticsearch-rails/issues/306#issuecomment-121817954 - __elasticsearch__.index_document(index: content_type.content_items_index_name) - end - - after_commit on: [:destroy] do - __elasticsearch__.delete_document(index: content_type.content_items_index_name) - end - - def as_indexed_json(options = {}) - json = as_json - field_items.each do |field_item| - field_type = field_item.field.field_type_instance(field_name: field_item.field.name) - json.merge!(field_type.field_item_as_indexed_json_for_field_type(field_item)) - end - json - end - end -end diff --git a/app/models/concerns/searchable_content_item_for_content_type.rb b/app/models/concerns/searchable_content_item_for_content_type.rb deleted file mode 100644 index bc2f7545e..000000000 --- a/app/models/concerns/searchable_content_item_for_content_type.rb +++ /dev/null @@ -1,50 +0,0 @@ -module SearchableContentItemForContentType - extend ActiveSupport::Concern - - included do - after_commit on: [:create] do - self.class.__elasticsearch__.create_index!(index: content_items_index_name, mappings: content_item_mappings) - end - - after_commit on: [:destroy] do - self.class.__elasticsearch__.delete_index!(index: content_items_index_name) - end - - after_commit on: [:update] do - self.class.__elasticsearch__.delete_index!(index: content_items_index_name) - self.class.__elasticsearch__.create_index!(index: content_items_index_name, mappings: content_item_mappings) - # TODO: implement & move import to ContentItem - #self.class.__elasticsearch__.import() - end - - def content_items_index_name - @content_items_index_name ||= "#{Rails.env}_content_type_#{name_id}_content_items" - end - end - - private - - def content_item_mappings - mappings = Elasticsearch::Model::Indexing::Mappings.new(content_items_index_name, { dynamic: false }) - mappings.indexes :tenant_id, type: :keyword, index: :not_analyzed - mappings.indexes :content_type_id, type: :keyword, index: :not_analyzed - mappings.indexes :creator_id, type: :keyword, index: :not_analyzed - mappings.indexes :updated_by_id, type: :keyword, index: :not_analyzed - mappings.indexes :state, analyzer: :keyword, index: :not_analyzed - mappings.indexes :created_at, type: :date, include_in_all: false - mappings.indexes :updated_at, type: :date, include_in_all: false - mappings.indexes :deleted_at, type: :date, include_in_all: false - - fields.each do |field| - mappings.indexes field.elasticsearch_mapping[:name], content_item_field_mappings(field) - end - - mappings - end - - def content_item_field_mappings(field) - mappings = { type: field.elasticsearch_mapping[:type] } - mappings[:analyzer] = field.elasticsearch_mapping[:analyzer] if field.elasticsearch_mapping[:analyzer] - mappings - end -end diff --git a/app/models/concerns/searchable_content_type.rb b/app/models/concerns/searchable_content_type.rb deleted file mode 100644 index e05bd16c1..000000000 --- a/app/models/concerns/searchable_content_type.rb +++ /dev/null @@ -1,9 +0,0 @@ -module SearchableContentType - extend ActiveSupport::Concern - - included do - include Searchable - - # TODO: ContentType mappings - end -end diff --git a/app/models/content_item.rb b/app/models/content_item.rb deleted file mode 100644 index d49680ac5..000000000 --- a/app/models/content_item.rb +++ /dev/null @@ -1,84 +0,0 @@ -class ContentItem < ApplicationRecord - include ActiveModel::Transitions - - include SearchableContentItem - include BelongsToTenant - - scope :last_updated_at, -> { order(updated_at: :desc).select('updated_at').first.updated_at } - - belongs_to :creator, class_name: 'User' - belongs_to :updated_by, class_name: 'User', optional: true - belongs_to :content_type - has_many :field_items, dependent: :destroy, autosave: true - - accepts_nested_attributes_for :field_items - - default_scope { order(created_at: :desc) } - - validates :creator, :content_type, presence: true - - state_machine do - state :draft - state :scheduled - - event :schedule do - transitions :to => :scheduled, :from => [:draft] - end - - event :draft do - transitions :to => :draft, :from => [:scheduled] - end - end - - def publish_state - PublishStateService.new.content_item_state(self) - end - - def rss_url(base_url, slug_field_id) # TODO: abstract RSS to separate app once API is implemented - slug = field_items.find_by_field_id(slug_field_id).data.values.join - "#{base_url}#{slug}" - end - - def rss_date(date_field_id) # TODO: abstract RSS to separate app once API is implemented - date = field_items.find_by_field_id(date_field_id).data["timestamp"] - Date.parse(date).rfc2822 - end - - def rss_author(field_id) # TODO: abstract RSS to separate app once API is implemented - author = field_items.find_by_field_id(field_id).data["author_name"] - "editorial@careerbuilder.com (#{author})" - end - - # FieldItem and State Convenience Methods. TODO: move to concern? transactions? - def method_missing(method_name, *arguments, &block) - super unless dynamic_method?(method_name) - - if dynamic_state_check?(method_name) - # Used to check state - allows for methods such as #published? and #expired? - # Will return true if the active_state corresponds to the name of the method - "#{publish_state.downcase}?" == method_name.to_s - else - # Used to query for any field on the relevant ContentType and return data from the content_item - field_items.find { |field_item| field_item.field.name.parameterize(separator: '_') == method_name.to_s }.data.values.first - end - end - - def respond_to_missing?(method_name, include_private = false) - dynamic_method?(method_name) || super - end - - private - - def dynamic_method?(method_name) - dynamic_state_check?(method_name) || has_field_item?(method_name) - end - - def dynamic_state_check?(method_name) - method_name.to_s.include? '?' - end - - # TODO: this logic effectively gets called multiple times (slow?) - how do we optimize or cache the result? - def has_field_item?(method_name) - field_items.any? { |field_item| field_item.field.name.parameterize(separator: '_') == method_name.to_s } - end -end diff --git a/app/models/content_type.rb b/app/models/content_type.rb deleted file mode 100644 index ae4289a33..000000000 --- a/app/models/content_type.rb +++ /dev/null @@ -1,38 +0,0 @@ -class ContentType < ApplicationRecord - include SearchableContentType - include SearchableContentItemForContentType - include BelongsToTenant - - validates :name, :creator, :contract, presence: true - validates_uniqueness_of :name, - :name_id, - scope: :tenant_id, - message: 'should be unique within a Tenant' - - belongs_to :creator, class_name: 'User' - belongs_to :contract - has_many :fields - has_many :content_items - has_many :contentable_decorators, as: :contentable - has_many :decorators, through: :contentable_decorators - - accepts_nested_attributes_for :fields - - # TODO: Extract to a concern - def self.permissions - Permission.select { |perm| perm.resource_type = self } - end - - # TODO: remove these garbage `_decorator` methods - def wizard_decorator - decorators.find_by_name("Wizard") - end - - def index_decorator - decorators.find_by_name("Index") - end - - def rss_decorator - decorators.find_by_name("Rss") - end -end diff --git a/app/models/contentable_decorator.rb b/app/models/contentable_decorator.rb deleted file mode 100644 index 7004f957b..000000000 --- a/app/models/contentable_decorator.rb +++ /dev/null @@ -1,6 +0,0 @@ -class ContentableDecorator < ApplicationRecord - include BelongsToTenant - - belongs_to :decorator - belongs_to :contentable, polymorphic: true -end diff --git a/app/models/contract.rb b/app/models/contract.rb deleted file mode 100644 index 51fbe3125..000000000 --- a/app/models/contract.rb +++ /dev/null @@ -1,10 +0,0 @@ -# TODO: Refactor -class Contract < ApplicationRecord - include BelongsToTenant - - validates :name, presence: true - - has_many :content_types - has_many :contentable_decorators, as: :contentable - has_many :decorators, through: :contentable_decorators -end diff --git a/app/models/cortex/application_record.rb b/app/models/cortex/application_record.rb new file mode 100644 index 000000000..a8614db3a --- /dev/null +++ b/app/models/cortex/application_record.rb @@ -0,0 +1,17 @@ +module Cortex + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + + acts_as_paranoid + + # A bug in ES-Rails 5.x is breaking document updates: + # https://github.com/elastic/elasticsearch-rails/issues/669 + after_commit :refresh_elasticsearch_index, on: :update + + private + + def refresh_elasticsearch_index + __elasticsearch__.index_document(refresh: true) if defined? __elasticsearch__ + end + end +end diff --git a/app/models/cortex/content_item.rb b/app/models/cortex/content_item.rb new file mode 100644 index 000000000..e3bd5e219 --- /dev/null +++ b/app/models/cortex/content_item.rb @@ -0,0 +1,86 @@ +module Cortex + class ContentItem < Cortex::ApplicationRecord + include ActiveModel::Transitions + + include Cortex::SearchableContentItem + include Cortex::BelongsToTenant + + scope :last_updated_at, -> { order(updated_at: :desc).select('updated_at').first.updated_at } + + belongs_to :creator, class_name: 'User' + belongs_to :updated_by, class_name: 'User', optional: true + belongs_to :content_type + has_many :field_items, dependent: :destroy, autosave: true + + accepts_nested_attributes_for :field_items + + default_scope { order(created_at: :desc) } + + validates :creator, :content_type, presence: true + + state_machine do + state :draft + state :scheduled + + event :schedule do + transitions :to => :scheduled, :from => [:draft] + end + + event :draft do + transitions :to => :draft, :from => [:scheduled] + end + end + + def publish_state + PublishStateService.new.content_item_state(self) + end + + def rss_url(base_url, slug_field_id) # TODO: abstract RSS to separate app once API is implemented + slug = field_items.find_by_field_id(slug_field_id).data.values.join + "#{base_url}#{slug}" + end + + def rss_date(date_field_id) # TODO: abstract RSS to separate app once API is implemented + date = field_items.find_by_field_id(date_field_id).data["timestamp"] + Date.parse(date).rfc2822 + end + + def rss_author(field_id) # TODO: abstract RSS to separate app once API is implemented + author = field_items.find_by_field_id(field_id).data["author_name"] + "editorial@careerbuilder.com (#{author})" + end + + # FieldItem and State Convenience Methods. TODO: move to concern? transactions? + def method_missing(method_name, *arguments, &block) + super unless dynamic_method?(method_name) + + if dynamic_state_check?(method_name) + # Used to check state - allows for methods such as #published? and #expired? + # Will return true if the active_state corresponds to the name of the method + "#{publish_state.downcase}?" == method_name.to_s + else + # Used to query for any field on the relevant ContentType and return data from the content_item + field_items.find { |field_item| field_item.field.name.parameterize(separator: '_') == method_name.to_s }.data.values.first + end + end + + def respond_to_missing?(method_name, include_private = false) + dynamic_method?(method_name) || super + end + + private + + def dynamic_method?(method_name) + dynamic_state_check?(method_name) || has_field_item?(method_name) + end + + def dynamic_state_check?(method_name) + method_name.to_s.include? '?' + end + + # TODO: this logic effectively gets called multiple times (slow?) - how do we optimize or cache the result? + def has_field_item?(method_name) + field_items.any? { |field_item| field_item.field.name.parameterize(separator: '_') == method_name.to_s } + end + end +end diff --git a/app/models/cortex/content_type.rb b/app/models/cortex/content_type.rb new file mode 100644 index 000000000..b6d4a5e72 --- /dev/null +++ b/app/models/cortex/content_type.rb @@ -0,0 +1,40 @@ +module Cortex + class ContentType < Cortex::ApplicationRecord + include Cortex::SearchableContentType + include Cortex::SearchableContentItemForContentType + include Cortex::BelongsToTenant + + validates :name, :creator, :contract, presence: true + validates_uniqueness_of :name, + :name_id, + scope: :tenant_id, + message: 'should be unique within a Tenant' + + belongs_to :creator, class_name: 'User' + belongs_to :contract + has_many :fields + has_many :content_items + has_many :contentable_decorators, as: :contentable + has_many :decorators, through: :contentable_decorators + + accepts_nested_attributes_for :fields + + # TODO: Extract to a concern + def self.permissions + Permission.select { |perm| perm.resource_type = self } + end + + # TODO: remove these garbage `_decorator` methods + def wizard_decorator + decorators.find_by_name("Wizard") + end + + def index_decorator + decorators.find_by_name("Index") + end + + def rss_decorator + decorators.find_by_name("Rss") + end + end +end diff --git a/app/models/cortex/contentable_decorator.rb b/app/models/cortex/contentable_decorator.rb new file mode 100644 index 000000000..6b61289a4 --- /dev/null +++ b/app/models/cortex/contentable_decorator.rb @@ -0,0 +1,8 @@ +module Cortex + class ContentableDecorator < Cortex::ApplicationRecord + include Cortex::BelongsToTenant + + belongs_to :decorator + belongs_to :contentable, polymorphic: true + end +end diff --git a/app/models/cortex/contract.rb b/app/models/cortex/contract.rb new file mode 100644 index 000000000..a79be0813 --- /dev/null +++ b/app/models/cortex/contract.rb @@ -0,0 +1,12 @@ +# TODO: Refactor +module Cortex + class Contract < Cortex::ApplicationRecord + include Cortex::BelongsToTenant + + validates :name, presence: true + + has_many :content_types + has_many :contentable_decorators, as: :contentable + has_many :decorators, through: :contentable_decorators + end +end diff --git a/app/models/cortex/decorator.rb b/app/models/cortex/decorator.rb new file mode 100644 index 000000000..be2ba73e9 --- /dev/null +++ b/app/models/cortex/decorator.rb @@ -0,0 +1,9 @@ +module Cortex + class Decorator < Cortex::ApplicationRecord + include Cortex::BelongsToTenant + + has_many :contentable_decorators + + validates :name, :data, presence: true + end +end diff --git a/app/models/cortex/field.rb b/app/models/cortex/field.rb new file mode 100644 index 000000000..5d69e5d80 --- /dev/null +++ b/app/models/cortex/field.rb @@ -0,0 +1,36 @@ +module Cortex + class Field < Cortex::ApplicationRecord + belongs_to :content_type, touch: true + has_many :field_items + has_many :content_items, through: :field_items + + validates :name, :content_type, :field_type, presence: true + validate :acceptable_field_type + validates_uniqueness_of :name, + :name_id, + scope: :content_type_id, + message: 'should be unique within a ContentType' + + def field_type_instance(options = {}) + field_type.camelize.constantize.new(options) + end + + def elasticsearch_mapping + field_type_instance(field_name: name).elasticsearch_mapping + end + + def tenant + content_type.tenant + end + + private + + def acceptable_field_type + begin + Cortex::FieldType.get_subtype_constant(field_type) + rescue NameError + errors.add(:field_type, 'must be an available field type') + end + end + end +end diff --git a/app/models/cortex/field_item.rb b/app/models/cortex/field_item.rb new file mode 100644 index 000000000..6a08d06b1 --- /dev/null +++ b/app/models/cortex/field_item.rb @@ -0,0 +1,54 @@ +module Cortex + class FieldItem < Cortex::ApplicationRecord + belongs_to :field + belongs_to :content_item, touch: true + + validates :field, :content_item, presence: true + validate :field_item_content_is_valid, if: :field_is_present + + def data=(data_hash) + # Reset @field_type_instance so that massaged data can be re-generated every time @data is set, not just on init + @field_type_instance = nil + super(field_type_instance(data_hash).data || data_hash) + end + + def tenant + content_item.tenant + end + + private + + def field_type_instance_params(data_hash) + # Carefully construct a params object so we don't trigger our fragile setters when a value is nil + params = { metadata: field.metadata.merge({ existing_data: data }), field: field, validations: field.validations } + params[:data] = data_hash if data_hash + params + end + + def field_type_instance(data_hash = nil) + field_type_class = FieldType.get_subtype_constant(field.field_type) + # data_before_typecast will give us a non-mutilated hash with Objects intact, just in case validations get called first + @field_type_instance ||= field_type_class.new(field_type_instance_params(data_hash)) + @field_type_instance.save + @field_type_instance + end + + def field_is_present + field.present? + end + + def field_item_content_is_valid + add_specific_errors unless field_item_validates + end + + def field_item_validates + field_type_instance.valid? + end + + def add_specific_errors + field_type_instance.errors.each do |k, v| + errors.add(field.name.to_sym, v) + end + end + end +end diff --git a/app/models/cortex/field_type.rb b/app/models/cortex/field_type.rb new file mode 100644 index 000000000..b67d9090e --- /dev/null +++ b/app/models/cortex/field_type.rb @@ -0,0 +1,44 @@ +module Cortex + class FieldType < Cortex::ApplicationRecord + extend ActiveSupport::DescendantsTracker + + attr_accessor :field_name, :field + attr_reader :data, :validations, :metadata + + def self.direct_descendant_names + direct_descendants.map { |descendant| descendant.name.underscore } + end + + def self.get_subtype_constant(descendant_name) + descendant_name.camelize.constantize + end + + def elasticsearch_mapping + raise 'Not implemented' + end + + def validations=(validations_hash) + @validations = validations_hash.deep_symbolize_keys if validations_hash + end + + def metadata=(metadata_hash) + @metadata = metadata_hash.deep_symbolize_keys if metadata_hash + end + + def acceptable_validations? + valid_types? && valid_options? + end + + def valid_types? + validations.all? do |type, options| + VALIDATION_TYPES.include?(type.to_sym) + end + end + + def valid_options? + validations.all? do |type, options| + self.send(VALIDATION_TYPES[type]) + end + end + end +end diff --git a/app/models/cortex/permission.rb b/app/models/cortex/permission.rb new file mode 100644 index 000000000..e13a1baa8 --- /dev/null +++ b/app/models/cortex/permission.rb @@ -0,0 +1,11 @@ +module Cortex + class Permission < Cortex::ApplicationRecord + has_and_belongs_to_many :roles + + validates :name, :resource_type, presence: true + + def resource + resource_type.constantize + end + end +end diff --git a/app/models/cortex/role.rb b/app/models/cortex/role.rb new file mode 100644 index 000000000..4531a3573 --- /dev/null +++ b/app/models/cortex/role.rb @@ -0,0 +1,12 @@ +module Cortex + class Role < Cortex::ApplicationRecord + scopify + + has_and_belongs_to_many :users + has_and_belongs_to_many :permissions + + validates :resource_type, + :inclusion => { :in => Rolify.resource_types }, + :allow_nil => true + end +end diff --git a/app/models/cortex/tenant.rb b/app/models/cortex/tenant.rb new file mode 100644 index 000000000..6b674dfda --- /dev/null +++ b/app/models/cortex/tenant.rb @@ -0,0 +1,41 @@ +module Cortex + class Tenant < Cortex::ApplicationRecord + acts_as_nested_set + + has_many :content_items + has_many :content_types + has_many :contracts + has_many :decorators + has_and_belongs_to_many :users + belongs_to :owner, class_name: 'User' + has_many :users, foreign_key: :active_tenant_id, class_name: 'User' + + validates_presence_of :name + validates_associated :owner + + validates_uniqueness_of :name, + :name_id + + alias_method :organization, :root + + def is_organization? + root? + end + + def has_children? + !leaf? + end + + def all_up_organization_for(klass) + self_and_ancestors.flat_map do |tenant| + tenant.public_send(klass.name.demodulize.underscore.pluralize).all + end + end + + def search_up_organization_for(klass, attribute, value) + all_up_organization_for(klass).select do |record| + record[attribute] == value + end + end + end +end diff --git a/app/models/cortex/user.rb b/app/models/cortex/user.rb new file mode 100644 index 000000000..cd0b2f0ff --- /dev/null +++ b/app/models/cortex/user.rb @@ -0,0 +1,77 @@ +require 'digest/md5' + +module Cortex + class User < Cortex::ApplicationRecord + include HasGravatar + include HasFirstnameLastname + + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable and :omniauthable + devise :database_authenticatable, :rememberable, :trackable, :validatable, :recoverable + rolify role_join_table_name: :roles_users # rolify doesn't obey lexical order for join table names + + has_and_belongs_to_many :tenants + belongs_to :active_tenant, class_name: 'Tenant', optional: true + has_many :content_items + + validates_presence_of :email, :firstname, :lastname + + before_destroy :prevent_consumed_deletion + + def active_tenant + super || tenants.order(:name).first + end + + def referenced? + [ContentItem].find do |resource| + true if resource.where(user: self).count > 0 + end + end + + def anonymous? + self.id == nil + end + + def has_permission?(resource, permission) + return true if self.is_superadmin? + + resource_class = resource.class + allowed_perms = allowed_permissions(resource_class, permission) + + if resource_class == Cortex::ContentType + allowed_perms.select { |perm| perm.resource_id == resource.id }.any? + else + allowed_perms.any? + end + end + + def gravatar + "//www.gravatar.com/avatar/#{Digest::MD5.hexdigest email}" + end + + def tenants_with_children + tenants.flat_map(&:self_and_descendants) + end + + private + + def allowed_permissions(resource_class, permission) + permissions.select { |perm| perm.resource_type == resource_class.to_s && perm.name == permission } + end + + def prevent_consumed_deletion + raise Cortex::Exceptions::ResourceConsumed if referenced? + end + + class << self + def authenticate(username, password) + user = User.find_by_email(username) + user && user.valid_password?(password) ? user : nil + end + + def anonymous + User.new + end + end + end +end diff --git a/app/models/decorator.rb b/app/models/decorator.rb deleted file mode 100644 index 2d2e7dd97..000000000 --- a/app/models/decorator.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Decorator < ApplicationRecord - include BelongsToTenant - - has_many :contentable_decorators - - validates :name, :data, presence: true -end diff --git a/app/models/field.rb b/app/models/field.rb deleted file mode 100644 index 991a7def2..000000000 --- a/app/models/field.rb +++ /dev/null @@ -1,34 +0,0 @@ -class Field < ApplicationRecord - belongs_to :content_type, touch: true - has_many :field_items - has_many :content_items, through: :field_items - - validates :name, :content_type, :field_type, presence: true - validate :acceptable_field_type - validates_uniqueness_of :name, - :name_id, - scope: :content_type_id, - message: 'should be unique within a ContentType' - - def field_type_instance(options={}) - field_type.camelize.constantize.new(options) - end - - def elasticsearch_mapping - field_type_instance(field_name: name).elasticsearch_mapping - end - - def tenant - content_type.tenant - end - - private - - def acceptable_field_type - begin - FieldType.get_subtype_constant(field_type) - rescue NameError - errors.add(:field_type, 'must be an available field type') - end - end -end diff --git a/app/models/field_item.rb b/app/models/field_item.rb deleted file mode 100644 index 13ab49a98..000000000 --- a/app/models/field_item.rb +++ /dev/null @@ -1,52 +0,0 @@ -class FieldItem < ApplicationRecord - belongs_to :field - belongs_to :content_item, touch: true - - validates :field, :content_item, presence: true - validate :field_item_content_is_valid, if: :field_is_present - - def data=(data_hash) - # Reset @field_type_instance so that massaged data can be re-generated every time @data is set, not just on init - @field_type_instance = nil - super(field_type_instance(data_hash).data || data_hash) - end - - def tenant - content_item.tenant - end - - private - - def field_type_instance_params(data_hash) - # Carefully construct a params object so we don't trigger our fragile setters when a value is nil - params = {metadata: field.metadata.merge({existing_data: data}), field: field, validations: field.validations} - params[:data] = data_hash if data_hash - params - end - - def field_type_instance(data_hash = nil) - field_type_class = FieldType.get_subtype_constant(field.field_type) - # data_before_typecast will give us a non-mutilated hash with Objects intact, just in case validations get called first - @field_type_instance ||= field_type_class.new(field_type_instance_params(data_hash)) - @field_type_instance.save - @field_type_instance - end - - def field_is_present - field.present? - end - - def field_item_content_is_valid - add_specific_errors unless field_item_validates - end - - def field_item_validates - field_type_instance.valid? - end - - def add_specific_errors - field_type_instance.errors.each do |k, v| - errors.add(field.name.to_sym, v) - end - end -end diff --git a/app/models/field_type.rb b/app/models/field_type.rb deleted file mode 100644 index ea6db6c3f..000000000 --- a/app/models/field_type.rb +++ /dev/null @@ -1,42 +0,0 @@ -class FieldType < ApplicationRecord - extend ActiveSupport::DescendantsTracker - - attr_accessor :field_name, :field - attr_reader :data, :validations, :metadata - - def self.direct_descendant_names - direct_descendants.map{ |descendant| descendant.name.underscore } - end - - def self.get_subtype_constant(descendant_name) - descendant_name.camelize.constantize - end - - def elasticsearch_mapping - raise 'Not implemented' - end - - def validations=(validations_hash) - @validations = validations_hash.deep_symbolize_keys if validations_hash - end - - def metadata=(metadata_hash) - @metadata = metadata_hash.deep_symbolize_keys if metadata_hash - end - - def acceptable_validations? - valid_types? && valid_options? - end - - def valid_types? - validations.all? do |type, options| - VALIDATION_TYPES.include?(type.to_sym) - end - end - - def valid_options? - validations.all? do |type, options| - self.send(VALIDATION_TYPES[type]) - end - end -end diff --git a/app/models/permission.rb b/app/models/permission.rb deleted file mode 100644 index a62ed641e..000000000 --- a/app/models/permission.rb +++ /dev/null @@ -1,9 +0,0 @@ -class Permission < ApplicationRecord - has_and_belongs_to_many :roles - - validates :name, :resource_type, presence: true - - def resource - resource_type.constantize - end -end diff --git a/app/models/role.rb b/app/models/role.rb deleted file mode 100644 index e56e6b0d2..000000000 --- a/app/models/role.rb +++ /dev/null @@ -1,10 +0,0 @@ -class Role < ApplicationRecord - scopify - - has_and_belongs_to_many :users - has_and_belongs_to_many :permissions - - validates :resource_type, - :inclusion => { :in => Rolify.resource_types }, - :allow_nil => true -end diff --git a/app/models/tenant.rb b/app/models/tenant.rb deleted file mode 100644 index 49362a48e..000000000 --- a/app/models/tenant.rb +++ /dev/null @@ -1,39 +0,0 @@ -class Tenant < ApplicationRecord - acts_as_nested_set - - has_many :content_items - has_many :content_types - has_many :contracts - has_many :decorators - has_and_belongs_to_many :users - belongs_to :owner, class_name: 'User' - has_many :users, foreign_key: :active_tenant_id, class_name: 'User' - - validates_presence_of :name - validates_associated :owner - - validates_uniqueness_of :name, - :name_id - - alias_method :organization, :root - - def is_organization? - root? - end - - def has_children? - !leaf? - end - - def all_up_organization_for(klass) - self_and_ancestors.flat_map do |tenant| - tenant.public_send(klass.name.underscore.pluralize).all - end - end - - def search_up_organization_for(klass, attribute, value) - all_up_organization_for(klass).select do |record| - record[attribute] == value - end - end -end diff --git a/app/models/user.rb b/app/models/user.rb deleted file mode 100644 index 89373e3a0..000000000 --- a/app/models/user.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'digest/md5' - -class User < ApplicationRecord - include HasGravatar - include HasFirstnameLastname - - # Include default devise modules. Others available are: - # :confirmable, :lockable, :timeoutable and :omniauthable - devise :database_authenticatable, :rememberable, :trackable, :validatable, :recoverable - rolify role_join_table_name: :roles_users # rolify doesn't obey lexical order for join table names - - has_and_belongs_to_many :tenants - belongs_to :active_tenant, class_name: 'Tenant', optional: true - has_many :content_items - - validates_presence_of :email, :firstname, :lastname - - before_destroy :prevent_consumed_deletion - - def active_tenant - super || tenants.order(:name).first - end - - def referenced? - [ContentItem].find do |resource| - true if resource.where(user: self).count > 0 - end - end - - def anonymous? - self.id == nil - end - - def has_permission?(resource, permission) - return true if self.is_superadmin? - - resource_class = resource.class - allowed_perms = allowed_permissions(resource_class, permission) - - if resource_class == ContentType - allowed_perms.select {|perm| perm.resource_id == resource.id}.any? - else - allowed_perms.any? - end - end - - def gravatar - "//www.gravatar.com/avatar/#{Digest::MD5.hexdigest email}" - end - - def tenants_with_children - tenants.flat_map(&:self_and_descendants) - end - - private - - def allowed_permissions(resource_class, permission) - permissions.select {|perm| perm.resource_type == resource_class.to_s && perm.name == permission} - end - - def prevent_consumed_deletion - raise Cortex::Exceptions::ResourceConsumed if referenced? - end - - class << self - def authenticate(username, password) - user = User.find_by_email(username) - user && user.valid_password?(password) ? user : nil - end - - def anonymous - User.new - end - end -end diff --git a/app/policies/application_policy.rb b/app/policies/application_policy.rb deleted file mode 100644 index 2a0bbc521..000000000 --- a/app/policies/application_policy.rb +++ /dev/null @@ -1,53 +0,0 @@ -class ApplicationPolicy - attr_reader :user, :record - - def initialize(user, record) - @user = user - @record = record - end - - def index? - false - end - - def show? - scope.where(:id => record.id).exists? - end - - def create? - false - end - - def new? - create? - end - - def update? - false - end - - def edit? - update? - end - - def destroy? - false - end - - def scope - Pundit.policy_scope!(user, record.class) - end - - class Scope - attr_reader :user, :scope - - def initialize(user, scope) - @user = user - @scope = scope - end - - def resolve - scope - end - end -end diff --git a/app/policies/content_item_policy.rb b/app/policies/content_item_policy.rb deleted file mode 100644 index ceadc9001..000000000 --- a/app/policies/content_item_policy.rb +++ /dev/null @@ -1,47 +0,0 @@ -class ContentItemPolicy - attr_reader :user, :content_item - - def initialize(user, content_item) - @user = user - @content_item = content_item - @content_type = @content_item.content_type - end - - def index? - has_permission?("Index") - end - - def show? - has_permission?("Show") - end - - def create? - has_permission?("Create") - end - - def new? - has_permission?("New") - end - - def update? - has_permission?("Update") - end - - def edit? - has_permission?("Edit") - end - - def destroy? - has_permission?("Destroy") - end - - def can_publish? - has_permission?("Publish") - end - - private - - def has_permission?(permission) - @user.has_permission?(@content_type, permission) - end -end diff --git a/app/policies/content_type_policy.rb b/app/policies/content_type_policy.rb deleted file mode 100644 index 2957dc4f8..000000000 --- a/app/policies/content_type_policy.rb +++ /dev/null @@ -1,36 +0,0 @@ -class ContentTypePolicy - attr_reader :user, :content_type - - def initialize(user, content_type) - @user = user - @content_type = content_type - end - - def index? - @user.is_superadmin? - end - - def show? - @user.is_superadmin? - end - - def create? - @user.is_superadmin? - end - - def new? - create? - end - - def update? - @user.is_superadmin? - end - - def edit? - update? - end - - def destroy? - @user.is_superadmin? - end -end diff --git a/app/policies/cortex/application_policy.rb b/app/policies/cortex/application_policy.rb new file mode 100644 index 000000000..b8d4e0f33 --- /dev/null +++ b/app/policies/cortex/application_policy.rb @@ -0,0 +1,55 @@ +module Cortex + class ApplicationPolicy + attr_reader :user, :record + + def initialize(user, record) + @user = user + @record = record + end + + def index? + false + end + + def show? + scope.where(:id => record.id).exists? + end + + def create? + false + end + + def new? + create? + end + + def update? + false + end + + def edit? + update? + end + + def destroy? + false + end + + def scope + Pundit.policy_scope!(user, record.class) + end + + class Scope + attr_reader :user, :scope + + def initialize(user, scope) + @user = user + @scope = scope + end + + def resolve + scope + end + end + end +end diff --git a/app/policies/cortex/content_item_policy.rb b/app/policies/cortex/content_item_policy.rb new file mode 100644 index 000000000..68a13c956 --- /dev/null +++ b/app/policies/cortex/content_item_policy.rb @@ -0,0 +1,49 @@ +module Cortex + class ContentItemPolicy + attr_reader :user, :content_item + + def initialize(user, content_item) + @user = user + @content_item = content_item + @content_type = @content_item.content_type + end + + def index? + has_permission?("Index") + end + + def show? + has_permission?("Show") + end + + def create? + has_permission?("Create") + end + + def new? + has_permission?("New") + end + + def update? + has_permission?("Update") + end + + def edit? + has_permission?("Edit") + end + + def destroy? + has_permission?("Destroy") + end + + def can_publish? + has_permission?("Publish") + end + + private + + def has_permission?(permission) + @user.has_permission?(@content_type, permission) + end + end +end diff --git a/app/policies/cortex/content_type_policy.rb b/app/policies/cortex/content_type_policy.rb new file mode 100644 index 000000000..e4a73f33b --- /dev/null +++ b/app/policies/cortex/content_type_policy.rb @@ -0,0 +1,38 @@ +module Cortex + class ContentTypePolicy + attr_reader :user, :content_type + + def initialize(user, content_type) + @user = user + @content_type = content_type + end + + def index? + @user.is_superadmin? + end + + def show? + @user.is_superadmin? + end + + def create? + @user.is_superadmin? + end + + def new? + create? + end + + def update? + @user.is_superadmin? + end + + def edit? + update? + end + + def destroy? + @user.is_superadmin? + end + end +end diff --git a/app/services/application_service.rb b/app/services/application_service.rb deleted file mode 100644 index ea399f117..000000000 --- a/app/services/application_service.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'dry-struct' - -class ApplicationService < Dry::Struct - constructor_type :schema - - attr_reader :errors -end diff --git a/app/services/content_item_service.rb b/app/services/content_item_service.rb deleted file mode 100644 index 821128cb7..000000000 --- a/app/services/content_item_service.rb +++ /dev/null @@ -1,86 +0,0 @@ -class ContentItemService < ApplicationService - include WidgetParsersHelper - - FieldItems = CoreTypes::Strict::Array.member(ApplicationTypes::FieldItem) - - attribute :id, CoreTypes::Strict::String.optional - attribute :content_item_params, Object - attribute :current_user, ApplicationTypes::User - attribute :creator, ApplicationTypes::User.optional - attribute :field_items, FieldItems - attribute :state, CoreTypes::Strict::String - class_attribute :form_fields - - def create - transact_and_refresh do - @content_item = ContentItem.new - self.form_fields = field_items_attributes.to_h.values.each_with_object({}) do |param, hash_object| - hash_object[param['field_id']] = param['data'] - end - content_item_params['field_items_attributes'].to_hash.each do |key, field_item_attributes| - field_item_attributes.delete('id') - @content_item.field_items << NewFieldItemTransaction.new.call(field_item_attributes).value - end - - content_item_params.delete('field_items_attributes') - @content_item.attributes = content_item_params.to_hash - @content_item.tenant = current_user.active_tenant # TODO: In future, grab from form/route, rather than current_user + perform authorization checks - end - end - - def update - transact_and_refresh do - @content_item = ContentItem.find(id) - content_item_params['field_items_attributes'].to_hash.each do |key, field_item_attributes| - @content_item.field_items << UpdateFieldItemTransaction.new.call(field_item_attributes).value - end - - content_item_params.delete('field_items_attributes') - @content_item.assign_attributes(content_item_attributes) - end - end - - private - - def transact_and_refresh - ActiveRecord::Base.transaction do - yield - parse_field_items! - @content_item.save! - execute_state_change(@content_item) - end - end - - def parse_field_items! - @content_item.field_items.each do |field_item| - if field_item.field.metadata && field_item.field.metadata['parse_widgets'] - parse_widgets!(field_item) - end - end - end - - def field_items_attributes - content_item_params["field_items_attributes"] - end - - def content_item_attributes - attributes = content_item_params || {creator: creator, content_type: content_type, field_items: field_items} - attributes.merge! latest_history_patch - end - - def latest_history_patch - history_patch = {} - history_patch.merge! last_updated_by - end - - def last_updated_by - {updated_by: current_user} - end - - def execute_state_change(content_item) - if state && content_item.can_transition?(state) - state_method = "#{state}!" - content_item.send(state_method) - end - end -end diff --git a/app/services/cortex/application_service.rb b/app/services/cortex/application_service.rb new file mode 100644 index 000000000..438b61ef0 --- /dev/null +++ b/app/services/cortex/application_service.rb @@ -0,0 +1,9 @@ +require 'dry-struct' + +module Cortex + class ApplicationService < Dry::Struct + constructor_type :schema + + attr_reader :errors + end +end diff --git a/app/services/cortex/content_item_service.rb b/app/services/cortex/content_item_service.rb new file mode 100644 index 000000000..801878602 --- /dev/null +++ b/app/services/cortex/content_item_service.rb @@ -0,0 +1,88 @@ +module Cortex + class ContentItemService < ApplicationService + include Cortex::WidgetParsersHelper + + FieldItems = CoreTypes::Strict::Array.member(ApplicationTypes::FieldItem) + + attribute :id, CoreTypes::Strict::String.optional + attribute :content_item_params, Object + attribute :current_user, ApplicationTypes::User + attribute :creator, ApplicationTypes::User.optional + attribute :field_items, FieldItems + attribute :state, CoreTypes::Strict::String + class_attribute :form_fields + + def create + transact_and_refresh do + @content_item = ContentItem.new + self.form_fields = field_items_attributes.to_h.values.each_with_object({}) do |param, hash_object| + hash_object[param['field_id']] = param['data'] + end + content_item_params['field_items_attributes'].to_hash.each do |key, field_item_attributes| + field_item_attributes.delete('id') + @content_item.field_items << NewFieldItemTransaction.new.call(field_item_attributes).value + end + + content_item_params.delete('field_items_attributes') + @content_item.attributes = content_item_params.to_hash + @content_item.tenant = current_user.active_tenant # TODO: In future, grab from form/route, rather than current_user + perform authorization checks + end + end + + def update + transact_and_refresh do + @content_item = Cortex::ContentItem.find(id) + content_item_params['field_items_attributes'].to_hash.each do |key, field_item_attributes| + @content_item.field_items << Cortex::UpdateFieldItemTransaction.new.call(field_item_attributes).value + end + + content_item_params.delete('field_items_attributes') + @content_item.assign_attributes(content_item_attributes) + end + end + + private + + def transact_and_refresh + ActiveRecord::Base.transaction do + yield + parse_field_items! + @content_item.save! + execute_state_change(@content_item) + end + end + + def parse_field_items! + @content_item.field_items.each do |field_item| + if field_item.field.metadata && field_item.field.metadata['parse_widgets'] + parse_widgets!(field_item) + end + end + end + + def field_items_attributes + content_item_params["field_items_attributes"] + end + + def content_item_attributes + attributes = content_item_params || { creator: creator, content_type: content_type, field_items: field_items } + attributes.merge! latest_history_patch + end + + def latest_history_patch + history_patch = {} + history_patch.merge! last_updated_by + end + + def last_updated_by + { updated_by: current_user } + end + + def execute_state_change(content_item) + if state && content_item.can_transition?(state) + state_method = "#{state}!" + content_item.send(state_method) + end + end + end +end diff --git a/app/services/cortex/index_decorator_service.rb b/app/services/cortex/index_decorator_service.rb new file mode 100644 index 000000000..47c2b4c28 --- /dev/null +++ b/app/services/cortex/index_decorator_service.rb @@ -0,0 +1,9 @@ +module Cortex + class IndexDecoratorService < ApplicationService + attribute :content_type, ApplicationTypes::ContentType + + def data + @content_type.index_decorator.data.deep_symbolize_keys + end + end +end diff --git a/app/services/cortex/publish_state_service.rb b/app/services/cortex/publish_state_service.rb new file mode 100644 index 000000000..e9bcc68a1 --- /dev/null +++ b/app/services/cortex/publish_state_service.rb @@ -0,0 +1,26 @@ +module Cortex + class PublishStateService < ApplicationService + def content_item_state(content_item) + @content_item = content_item + + active_state + end + + private + + def active_state + sorted_field_items.each do |field_item| + if !field_item.data['timestamp'].blank? && DateTime.now > DateTime.parse(field_item.data["timestamp"]) + return field_item.field.metadata["state"] + break + end + end + + return @content_item.state.titleize + end + + def sorted_field_items + @sorted_field_items ||= @content_item.field_items.select { |fi| fi.field.field_type_instance.is_a?(DateTimeFieldType) && !fi.field.metadata["state"].nil? }.sort_by { |a| a.data["timestamp"] }.reverse + end + end +end diff --git a/app/services/cortex/wizard_decorator_service.rb b/app/services/cortex/wizard_decorator_service.rb new file mode 100644 index 000000000..3de0c9445 --- /dev/null +++ b/app/services/cortex/wizard_decorator_service.rb @@ -0,0 +1,9 @@ +module Cortex + class WizardDecoratorService < ApplicationService + attribute :content_item, ApplicationTypes::ContentItem + + def data + @content_item.content_type.wizard_decorator.data.deep_symbolize_keys + end + end +end diff --git a/app/services/index_decorator_service.rb b/app/services/index_decorator_service.rb deleted file mode 100644 index a499bca5e..000000000 --- a/app/services/index_decorator_service.rb +++ /dev/null @@ -1,7 +0,0 @@ -class IndexDecoratorService < ApplicationService - attribute :content_type, ApplicationTypes::ContentType - - def data - @content_type.index_decorator.data.deep_symbolize_keys - end -end diff --git a/app/services/publish_state_service.rb b/app/services/publish_state_service.rb deleted file mode 100644 index 9b0ee590e..000000000 --- a/app/services/publish_state_service.rb +++ /dev/null @@ -1,24 +0,0 @@ -class PublishStateService < ApplicationService - def content_item_state(content_item) - @content_item = content_item - - active_state - end - - private - - def active_state - sorted_field_items.each do |field_item| - if !field_item.data['timestamp'].blank? && DateTime.now > DateTime.parse(field_item.data["timestamp"]) - return field_item.field.metadata["state"] - break - end - end - - return @content_item.state.titleize - end - - def sorted_field_items - @sorted_field_items ||= @content_item.field_items.select { |fi| fi.field.field_type_instance.is_a?(DateTimeFieldType) && !fi.field.metadata["state"].nil? }.sort_by{ |a| a.data["timestamp"] }.reverse - end -end diff --git a/app/services/wizard_decorator_service.rb b/app/services/wizard_decorator_service.rb deleted file mode 100644 index 7b645934d..000000000 --- a/app/services/wizard_decorator_service.rb +++ /dev/null @@ -1,7 +0,0 @@ -class WizardDecoratorService < ApplicationService - attribute :content_item, ApplicationTypes::ContentItem - - def data - @content_item.content_type.wizard_decorator.data.deep_symbolize_keys - end -end diff --git a/app/transactions/application_transaction.rb b/app/transactions/application_transaction.rb deleted file mode 100644 index 613e83b5e..000000000 --- a/app/transactions/application_transaction.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'dry/transaction' - -class ApplicationTransaction - include Dry::Transaction -end diff --git a/app/transactions/concerns/cortex/field_item_transactor.rb b/app/transactions/concerns/cortex/field_item_transactor.rb new file mode 100644 index 000000000..4ab11320f --- /dev/null +++ b/app/transactions/concerns/cortex/field_item_transactor.rb @@ -0,0 +1,28 @@ +module Cortex + module FieldItemTransactor + extend ActiveSupport::Concern + + included do + # TODO: DRY all this up + def plugin_new_transaction_klass(field_item) + "new_#{field_type_name(field_item)}_field_item_transaction".classify.safe_constantize + end + + def plugin_update_transaction_klass(field_item) + "update_#{field_type_name(field_item)}_field_item_transaction".classify.safe_constantize + end + + def field_type_name(field_item) + field_item.field.field_type.chomp '_field_type' + end + + def transact_new(field_item) + plugin_new_transaction_klass(field_item)&.new&.call(field_item)&.value + end + + def transact_update(field_item) + plugin_update_transaction_klass(field_item)&.new&.call(field_item)&.value + end + end + end +end diff --git a/app/transactions/concerns/field_item_transactor.rb b/app/transactions/concerns/field_item_transactor.rb deleted file mode 100644 index 257fe9398..000000000 --- a/app/transactions/concerns/field_item_transactor.rb +++ /dev/null @@ -1,26 +0,0 @@ -module FieldItemTransactor - extend ActiveSupport::Concern - - included do - # TODO: DRY all this up - def plugin_new_transaction_klass(field_item) - "new_#{field_type_name(field_item)}_field_item_transaction".classify.safe_constantize - end - - def plugin_update_transaction_klass(field_item) - "update_#{field_type_name(field_item)}_field_item_transaction".classify.safe_constantize - end - - def field_type_name(field_item) - field_item.field.field_type.chomp '_field_type' - end - - def transact_new(field_item) - plugin_new_transaction_klass(field_item)&.new&.call(field_item)&.value - end - - def transact_update(field_item) - plugin_update_transaction_klass(field_item)&.new&.call(field_item)&.value - end - end -end diff --git a/app/transactions/cortex/application_transaction.rb b/app/transactions/cortex/application_transaction.rb new file mode 100644 index 000000000..a1432a5a4 --- /dev/null +++ b/app/transactions/cortex/application_transaction.rb @@ -0,0 +1,7 @@ +require 'dry/transaction' + +module Cortex + class ApplicationTransaction + include Dry::Transaction + end +end diff --git a/app/transactions/cortex/get_content_item_transaction.rb b/app/transactions/cortex/get_content_item_transaction.rb new file mode 100644 index 000000000..fbdc58d13 --- /dev/null +++ b/app/transactions/cortex/get_content_item_transaction.rb @@ -0,0 +1,4 @@ +module Cortex + class GetContentItemTransaction < Cortex::ApplicationTransaction + end +end diff --git a/app/transactions/cortex/get_content_items_for_content_type_transaction.rb b/app/transactions/cortex/get_content_items_for_content_type_transaction.rb new file mode 100644 index 000000000..edcb64aa6 --- /dev/null +++ b/app/transactions/cortex/get_content_items_for_content_type_transaction.rb @@ -0,0 +1,34 @@ +require 'elasticsearch/dsl' + +module Cortex + class GetContentItemsForContentTypeTransaction < Cortex::ApplicationTransaction + include Elasticsearch::DSL + + step :search + + def search(params) + tenant_id = params[:args][:tenant_id] || params[:active_tenant].id + content_type_id = params[:content_type].id + + definition = Elasticsearch::DSL::Search.search { + query do + bool do + filter do + term tenant_id: tenant_id + end + filter do + term content_type_id: content_type_id + end + end + end + sort created_at: { + order: 'desc' + } + } + + results = Cortex::ContentItem.search(definition, index: params[:content_type].content_items_index_name) + # TODO: basic pagination + Right(results.records) + end + end +end diff --git a/app/transactions/cortex/new_field_item_transaction.rb b/app/transactions/cortex/new_field_item_transaction.rb new file mode 100644 index 000000000..96df04f0f --- /dev/null +++ b/app/transactions/cortex/new_field_item_transaction.rb @@ -0,0 +1,16 @@ +module Cortex + class NewFieldItemTransaction < Cortex::ApplicationTransaction + include Cortex::FieldItemTransactor + + step :init + step :process_plugin_transaction + + def init(field_item_attributes) + Right(Cortex::FieldItem.new(field_item_attributes)) + end + + def process_plugin_transaction(field_item) + Right(transact_new(field_item) || field_item) + end + end +end diff --git a/app/transactions/cortex/update_field_item_transaction.rb b/app/transactions/cortex/update_field_item_transaction.rb new file mode 100644 index 000000000..8ca2a68bb --- /dev/null +++ b/app/transactions/cortex/update_field_item_transaction.rb @@ -0,0 +1,23 @@ +module Cortex + class UpdateFieldItemTransaction < Cortex::ApplicationTransaction + include Cortex::FieldItemTransactor + + step :init + step :process_plugin_transaction + + def init(field_item_attributes) + field_item = FieldItem.find_by_id(field_item_attributes['id']) + + if field_item + field_item.assign_attributes(field_item_attributes) + Right(field_item) + else + Left(:not_found) + end + end + + def process_plugin_transaction(field_item) + Right(transact_update(field_item) || field_item) + end + end +end diff --git a/app/transactions/get_content_item_transaction.rb b/app/transactions/get_content_item_transaction.rb deleted file mode 100644 index 1b3522cee..000000000 --- a/app/transactions/get_content_item_transaction.rb +++ /dev/null @@ -1,2 +0,0 @@ -class GetContentItemTransaction < ApplicationTransaction -end diff --git a/app/transactions/get_content_items_for_content_type_transaction.rb b/app/transactions/get_content_items_for_content_type_transaction.rb deleted file mode 100644 index c08543dd7..000000000 --- a/app/transactions/get_content_items_for_content_type_transaction.rb +++ /dev/null @@ -1,30 +0,0 @@ -class GetContentItemsForContentTypeTransaction < ApplicationTransaction - include Elasticsearch::DSL - - step :search - - def search(params) - tenant_id = params[:args][:tenant_id] || params[:active_tenant].id - content_type_id = params[:content_type].id - - definition = Elasticsearch::DSL::Search.search { - query do - bool do - filter do - term tenant_id: tenant_id - end - filter do - term content_type_id: content_type_id - end - end - end - sort created_at: { - order: 'desc' - } - } - - results = ContentItem.search(definition, index: params[:content_type].content_items_index_name) - # TODO: basic pagination - Right(results.records) - end -end diff --git a/app/transactions/new_field_item_transaction.rb b/app/transactions/new_field_item_transaction.rb deleted file mode 100644 index 1d5240eb8..000000000 --- a/app/transactions/new_field_item_transaction.rb +++ /dev/null @@ -1,14 +0,0 @@ -class NewFieldItemTransaction < ApplicationTransaction - include FieldItemTransactor - - step :init - step :process_plugin_transaction - - def init(field_item_attributes) - Right(FieldItem.new(field_item_attributes)) - end - - def process_plugin_transaction(field_item) - Right(transact_new(field_item) || field_item) - end -end diff --git a/app/transactions/update_field_item_transaction.rb b/app/transactions/update_field_item_transaction.rb deleted file mode 100644 index f70f9dde8..000000000 --- a/app/transactions/update_field_item_transaction.rb +++ /dev/null @@ -1,21 +0,0 @@ -class UpdateFieldItemTransaction < ApplicationTransaction - include FieldItemTransactor - - step :init - step :process_plugin_transaction - - def init(field_item_attributes) - field_item = FieldItem.find_by_id(field_item_attributes['id']) - - if field_item - field_item.assign_attributes(field_item_attributes) - Right(field_item) - else - Left(:not_found) - end - end - - def process_plugin_transaction(field_item) - Right(transact_update(field_item) || field_item) - end -end diff --git a/app/types/application_types.rb b/app/types/application_types.rb deleted file mode 100644 index 4e113becf..000000000 --- a/app/types/application_types.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'dry-types' - -module ApplicationTypes - User = Dry::Types::Definition.new(::User) - ContentType = Dry::Types::Definition.new(::ContentType) - ContentItem = Dry::Types::Definition.new(::ContentItem) - FieldType = Dry::Types::Definition.new(::FieldType) - FieldItem = Dry::Types::Definition.new(::UpdateFieldItemTransaction) - Field = Dry::Types::Definition.new(::Field) -end diff --git a/app/types/core_types.rb b/app/types/core_types.rb deleted file mode 100644 index 09bbe99cc..000000000 --- a/app/types/core_types.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'dry-types' - -module CoreTypes - include Dry::Types.module -end diff --git a/app/types/cortex/application_types.rb b/app/types/cortex/application_types.rb new file mode 100644 index 000000000..e46c47488 --- /dev/null +++ b/app/types/cortex/application_types.rb @@ -0,0 +1,12 @@ +require 'dry-types' + +module Cortex + module ApplicationTypes + User = Dry::Types::Definition.new(::Cortex::User) + ContentType = Dry::Types::Definition.new(::Cortex::ContentType) + ContentItem = Dry::Types::Definition.new(::Cortex::ContentItem) + FieldType = Dry::Types::Definition.new(::Cortex::FieldType) + FieldItem = Dry::Types::Definition.new(::Cortex::UpdateFieldItemTransaction) + Field = Dry::Types::Definition.new(::Cortex::Field) + end +end diff --git a/app/types/cortex/core_types.rb b/app/types/cortex/core_types.rb new file mode 100644 index 000000000..fcf3288f5 --- /dev/null +++ b/app/types/cortex/core_types.rb @@ -0,0 +1,7 @@ +require 'dry-types' + +module Cortex + module CoreTypes + include Dry::Types.module + end +end diff --git a/app/views/content_items/_grid.html.haml b/app/views/content_items/_grid.html.haml deleted file mode 100644 index 2bd078240..000000000 --- a/app/views/content_items/_grid.html.haml +++ /dev/null @@ -1 +0,0 @@ -= cell("index", @index, context: { content_type: content_type, content_items: @content_items }).(:index) diff --git a/app/views/authentication/passwords/new.html.haml b/app/views/cortex/authentication/passwords/new.html.haml similarity index 90% rename from app/views/authentication/passwords/new.html.haml rename to app/views/cortex/authentication/passwords/new.html.haml index 4fb3d7ce3..6f9cf3892 100644 --- a/app/views/authentication/passwords/new.html.haml +++ b/app/views/cortex/authentication/passwords/new.html.haml @@ -10,4 +10,4 @@ = f.email_field :email, autofocus: true .actions = f.submit "Send me reset password instructions" - = render "devise/shared/links" + = render 'cortex/authentication/shared/links' diff --git a/app/views/authentication/sessions/new.html.haml b/app/views/cortex/authentication/sessions/new.html.haml similarity index 94% rename from app/views/authentication/sessions/new.html.haml rename to app/views/cortex/authentication/sessions/new.html.haml index 3404428e4..f07cbef3c 100644 --- a/app/views/authentication/sessions/new.html.haml +++ b/app/views/cortex/authentication/sessions/new.html.haml @@ -17,4 +17,4 @@ %br/ = f.submit "Log in", class: 'mdl-button mdl-js-button mdl-button--raised mdl-button--colored' - = render "authentication/shared/links" + = render 'cortex/authentication/shared/links' diff --git a/app/views/authentication/shared/_links.html.haml b/app/views/cortex/authentication/shared/_links.html.haml similarity index 100% rename from app/views/authentication/shared/_links.html.haml rename to app/views/cortex/authentication/shared/_links.html.haml diff --git a/app/views/content_items/_form.html.haml b/app/views/cortex/content_items/_form.html.haml similarity index 89% rename from app/views/content_items/_form.html.haml rename to app/views/cortex/content_items/_form.html.haml index 2cb210556..79a853942 100644 --- a/app/views/content_items/_form.html.haml +++ b/app/views/cortex/content_items/_form.html.haml @@ -5,7 +5,7 @@ - if @content_type.name == 'Media' = react_component('CortexApp', props: { temporary_render: 'Wizard' }) - else - = cell('wizard', @wizard, context: { content_item: @content_item, form: form }).() + = cell(Cortex::WizardCell, @wizard, context: { content_item: @content_item, form: form }).() %footer.mdl-grid .mdl-cell.mdl-cell--12-col diff --git a/app/views/cortex/content_items/_grid.html.haml b/app/views/cortex/content_items/_grid.html.haml new file mode 100644 index 000000000..5300fe901 --- /dev/null +++ b/app/views/cortex/content_items/_grid.html.haml @@ -0,0 +1 @@ += cell(Cortex::IndexCell, @index, context: { content_type: content_type, content_items: @content_items }).(:index) diff --git a/app/views/content_items/edit.html.haml b/app/views/cortex/content_items/edit.html.haml similarity index 100% rename from app/views/content_items/edit.html.haml rename to app/views/cortex/content_items/edit.html.haml diff --git a/app/views/content_items/index.html.haml b/app/views/cortex/content_items/index.html.haml similarity index 100% rename from app/views/content_items/index.html.haml rename to app/views/cortex/content_items/index.html.haml diff --git a/app/views/content_items/new.html.haml b/app/views/cortex/content_items/new.html.haml similarity index 100% rename from app/views/content_items/new.html.haml rename to app/views/cortex/content_items/new.html.haml diff --git a/app/views/content_types/index.html.haml b/app/views/cortex/content_types/index.html.haml similarity index 100% rename from app/views/content_types/index.html.haml rename to app/views/cortex/content_types/index.html.haml diff --git a/app/views/content_types/new.html.haml b/app/views/cortex/content_types/new.html.haml similarity index 100% rename from app/views/content_types/new.html.haml rename to app/views/cortex/content_types/new.html.haml diff --git a/app/views/cortex/dashboards/index.html.haml b/app/views/cortex/dashboards/index.html.haml new file mode 100644 index 000000000..6a3638533 --- /dev/null +++ b/app/views/cortex/dashboards/index.html.haml @@ -0,0 +1,6 @@ += render 'cortex/partials/quick_links' + +- unless news_feed.blank? + .mdl-grid + .mdl-cell.mdl-cell--12-col + = render 'cortex/partials/notes' diff --git a/app/views/graphql/ide.haml b/app/views/cortex/graphql/ide.haml similarity index 100% rename from app/views/graphql/ide.haml rename to app/views/cortex/graphql/ide.haml diff --git a/app/views/partials/_breadcrumb.html.haml b/app/views/cortex/partials/_breadcrumb.html.haml similarity index 100% rename from app/views/partials/_breadcrumb.html.haml rename to app/views/cortex/partials/_breadcrumb.html.haml diff --git a/app/views/partials/_cortex_redux_state.html.haml b/app/views/cortex/partials/_cortex_redux_state.html.haml similarity index 100% rename from app/views/partials/_cortex_redux_state.html.haml rename to app/views/cortex/partials/_cortex_redux_state.html.haml diff --git a/app/views/partials/_flash.html.haml b/app/views/cortex/partials/_flash.html.haml similarity index 81% rename from app/views/partials/_flash.html.haml rename to app/views/cortex/partials/_flash.html.haml index d73b201ab..41b6372c4 100644 --- a/app/views/partials/_flash.html.haml +++ b/app/views/cortex/partials/_flash.html.haml @@ -14,17 +14,8 @@ %button.mdl-snackbar__action{ type: 'button' } - elsif flash['success'] .mdl-snackbar.mdl-js-snackbar.snackbar__message.mdl-snackbar--active.flash--success{ 'aria-atomic' => 'true', 'aria-live' => 'assertive', 'aria-relevant' => 'text' } - .confetti.confetti--left - - 50.times do - %i - .confetti.confetti--center - - 50.times do - %i - .confetti.confetti--right - - 50.times do - %i .mdl-snackbar__text - = image_tag('check.svg', size: '18x18') + %i.material-icons check = value %button.mdl-snackbar__action{ type: 'button' } - else diff --git a/app/views/partials/_media_dialogs.html.haml b/app/views/cortex/partials/_media_dialogs.html.haml similarity index 56% rename from app/views/partials/_media_dialogs.html.haml rename to app/views/cortex/partials/_media_dialogs.html.haml index 214f15173..0aead173a 100644 --- a/app/views/partials/_media_dialogs.html.haml +++ b/app/views/cortex/partials/_media_dialogs.html.haml @@ -2,12 +2,12 @@ %dialog.mdl-dialog#wysiwyg %h4.mdl-dialog__title Insert Media .mdl-dialog__content - = cell('index', media_index, context: { content_type: content_type, content_items: media_content_items, popup: 'wysiwyg' }).(:index) + = cell(Cortex::IndexCell, media_index, context: { content_type: content_type, content_items: media_content_items, popup: 'wysiwyg' }).(:index) .mdl-dialog__actions .mdl-button.mdl-js-button.mdl-button--raised.close-dialog Close %dialog.mdl-dialog#featured %h4.mdl-dialog__title Select Media .mdl-dialog__content - = cell('index', media_index, context: { content_type: content_type, content_items: media_image_content_items, popup: 'featured' }).(:index) + = cell(Cortex::IndexCell, media_index, context: { content_type: content_type, content_items: media_image_content_items, popup: 'featured' }).(:index) .mdl-dialog__actions .mdl-button.mdl-js-button.mdl-button--raised.close-dialog Close diff --git a/app/views/partials/_navigation.html.haml b/app/views/cortex/partials/_navigation.html.haml similarity index 82% rename from app/views/partials/_navigation.html.haml rename to app/views/cortex/partials/_navigation.html.haml index 046cf70a1..3c3b82c8b 100644 --- a/app/views/partials/_navigation.html.haml +++ b/app/views/cortex/partials/_navigation.html.haml @@ -3,7 +3,7 @@ %i.material-icons{role: 'presentation'}> dashboard %span.nav__item-name Dashboard - = cell(:content_type, collection: current_user.active_tenant.all_up_organization_for(ContentType)).(:nav) + = cell(Cortex::ContentTypeCell, collection: current_user.active_tenant.all_up_organization_for(Cortex::ContentType)).(:nav) .mdl-layout-spacer = link_to graphql_ide_path, class: 'mdl-navigation__link' do %i.material-icons{role: 'presentation'} code diff --git a/app/views/partials/_notes.html.haml b/app/views/cortex/partials/_notes.html.haml similarity index 100% rename from app/views/partials/_notes.html.haml rename to app/views/cortex/partials/_notes.html.haml diff --git a/app/views/partials/_quick_links.html.haml b/app/views/cortex/partials/_quick_links.html.haml similarity index 76% rename from app/views/partials/_quick_links.html.haml rename to app/views/cortex/partials/_quick_links.html.haml index f7911132c..d32ada861 100644 --- a/app/views/partials/_quick_links.html.haml +++ b/app/views/cortex/partials/_quick_links.html.haml @@ -5,9 +5,9 @@ .jumbo-button %span.jumbo-button__content %p.jumbo-button__content__icon - = image_tag 'icons/blog.png' + = image_tag 'cortex/icons/blog.png' %p.jumbo-button__content__add - = image_tag 'icons/add.svg' + = image_tag 'cortex/icons/add.svg' %p Create a New Post - if media_content_type @@ -16,7 +16,7 @@ .jumbo-button %span.jumbo-button__content %p.jumbo-button__content__icon - = image_tag 'icons/media.png' + = image_tag 'cortex/icons/media.png' %p.jumbo-button__content__add - = image_tag 'icons/add.svg' + = image_tag 'cortex/icons/add.svg' %p Upload Media diff --git a/app/views/partials/_sidebar.html.haml b/app/views/cortex/partials/_sidebar.html.haml similarity index 51% rename from app/views/partials/_sidebar.html.haml rename to app/views/cortex/partials/_sidebar.html.haml index 847dbce17..4fbb3b26e 100644 --- a/app/views/partials/_sidebar.html.haml +++ b/app/views/cortex/partials/_sidebar.html.haml @@ -1,7 +1,7 @@ .layout__sidebar.mdl-layout__drawer %header.sidebar__header - = link_to root_path, class: 'sidebar__root-link' do - = image_tag 'logo.svg', class: 'sidebar__brand-logo' + = link_to cortex.root_path, class: 'sidebar__root-link' do + = image_tag 'cortex/logo.svg', class: 'sidebar__brand-logo' %h1.sidebar__brand-name Cortex - = render 'partials/navigation' + = render 'cortex/partials/navigation' = react_component('CortexApp', props: { temporary_render: 'TenantSwitcher' }) diff --git a/app/views/cortex/partials/_topbar.html.haml b/app/views/cortex/partials/_topbar.html.haml new file mode 100644 index 000000000..11201f0c0 --- /dev/null +++ b/app/views/cortex/partials/_topbar.html.haml @@ -0,0 +1,5 @@ +%header.mdl-layout__header + .mdl-layout__header-row + = render 'cortex/partials/breadcrumb' + .mdl-layout-spacer + = render 'cortex/partials/user_control' diff --git a/app/views/partials/_user_control.html.haml b/app/views/cortex/partials/_user_control.html.haml similarity index 100% rename from app/views/partials/_user_control.html.haml rename to app/views/cortex/partials/_user_control.html.haml diff --git a/app/views/partials/authentication/_header.html.haml b/app/views/cortex/partials/authentication/_header.html.haml similarity index 61% rename from app/views/partials/authentication/_header.html.haml rename to app/views/cortex/partials/authentication/_header.html.haml index c0c6b8ec5..3352cac81 100644 --- a/app/views/partials/authentication/_header.html.haml +++ b/app/views/cortex/partials/authentication/_header.html.haml @@ -1,4 +1,4 @@ %header.authentication__header - = image_tag 'logo.svg', class: 'logo' + = image_tag 'cortex/logo.svg', class: 'logo' %h1.authentication__header-text Cortex diff --git a/app/views/partials/trackers/_google_analytics.html.haml b/app/views/cortex/partials/trackers/_google_analytics.html.haml similarity index 84% rename from app/views/partials/trackers/_google_analytics.html.haml rename to app/views/cortex/partials/trackers/_google_analytics.html.haml index 81e03c7ef..aaf25443d 100644 --- a/app/views/partials/trackers/_google_analytics.html.haml +++ b/app/views/cortex/partials/trackers/_google_analytics.html.haml @@ -1,6 +1,6 @@ :javascript var _gaq = _gaq || []; - _gaq.push(['_setAccount', '#{extra_config.google_analytics_id}']); + _gaq.push(['_setAccount', '#{extra_config[:google_analytics_id]}']); _gaq.push(['_trackPageview']); (function() { diff --git a/app/views/partials/trackers/_qualtrics.html.haml b/app/views/cortex/partials/trackers/_qualtrics.html.haml similarity index 78% rename from app/views/partials/trackers/_qualtrics.html.haml rename to app/views/cortex/partials/trackers/_qualtrics.html.haml index 238bd4050..7f66c0fa6 100644 --- a/app/views/partials/trackers/_qualtrics.html.haml +++ b/app/views/cortex/partials/trackers/_qualtrics.html.haml @@ -5,6 +5,6 @@ this.check=function(){var a=this.get(f);if(a)a=a.split(":");else if(100!=e)"v"==h&&(e=Math.random()>=e/100?0:100),a=[h,e,0],this.set(f,a.join(":"));else return!0;var c=a[1];if(100==c)return!0;switch(a[0]){case "v":return!1;case "r":return c=a[2]%Math.floor(100/c),a[2]++,this.set(f,a.join(":")),!c}return!0}; this.go=function(){if(this.check()){var a=document.createElement("script");a.type="text/javascript";a.src=g+ "&t=" + (new Date()).getTime();document.body&&document.body.appendChild(a)}}; this.start=function(){var a=this;window.addEventListener?window.addEventListener("load",function(){a.go()},!1):window.attachEvent&&window.attachEvent("onload",function(){a.go()})}}; - try{(new g(100,"r","QSI_S_#{extra_config.qualtrics_id}","//#{qualtrics_domain}-careerbuilder.siteintercept.qualtrics.com/WRSiteInterceptEngine/?Q_ZID=#{extra_config.qualtrics_id}&Q_LOC="+encodeURIComponent(window.location.href))).start()}catch(i){}})(); + try{(new g(100,"r","QSI_S_#{extra_config[:qualtrics_id]}","//#{qualtrics_domain}-careerbuilder.siteintercept.qualtrics.com/WRSiteInterceptEngine/?Q_ZID=#{extra_config[:qualtrics_id]}&Q_LOC="+encodeURIComponent(window.location.href))).start()}catch(i){}})(); -%div{ id: "#{extra_config.qualtrics_id}" } +%div{ id: "#{extra_config[:qualtrics_id]}" } diff --git a/app/views/password_reset_mailer/send_password_reset.html.erb b/app/views/cortex/password_reset_mailer/send_password_reset.html.erb similarity index 100% rename from app/views/password_reset_mailer/send_password_reset.html.erb rename to app/views/cortex/password_reset_mailer/send_password_reset.html.erb diff --git a/app/views/password_reset_mailer/send_password_reset.text.erb b/app/views/cortex/password_reset_mailer/send_password_reset.text.erb similarity index 100% rename from app/views/password_reset_mailer/send_password_reset.text.erb rename to app/views/cortex/password_reset_mailer/send_password_reset.text.erb diff --git a/app/views/rss/v2/rss/index.rss.builder b/app/views/cortex/rss/v2/rss/index.rss.builder similarity index 100% rename from app/views/rss/v2/rss/index.rss.builder rename to app/views/cortex/rss/v2/rss/index.rss.builder diff --git a/app/views/dashboards/index.html.haml b/app/views/dashboards/index.html.haml deleted file mode 100644 index 1a10f9b00..000000000 --- a/app/views/dashboards/index.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -= render 'partials/quick_links' - -- unless news_feed.blank? - .mdl-grid - .mdl-cell.mdl-cell--12-col - = render 'partials/notes' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml deleted file mode 100644 index bf4a71997..000000000 --- a/app/views/layouts/application.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -!!! 5 -%html - %head - %meta{content: 'IE=edge', 'http-equiv' => 'X-UA-Compatible'} - %meta{charset: 'utf-8'} - %meta{name: 'viewport', content: 'width=device-width, initial-scale=1.0, minimum-scale=1.0'} - %title - = title - = favicon_link_tag 'favicon.ico' - = stylesheet_link_tag :application, {media: 'all', 'data-turbolinks-track': 'reload'} - // = stylesheet_pack_tag 'cortex-bundle', {media: 'all', 'data-turbolinks-track': 'reload'} # Enable once assets migrated - = csrf_meta_tags - %body - = render 'partials/media_dialogs' - = render 'partials/cortex_redux_state' - #layout_wrapper{class: ('sidebar--collapsed' unless current_page? root_path)} - .layout__container.mdl-js-layout.mdl-layout--fixed-drawer - = render 'partials/sidebar' - .layout__topbar-content__container.mdl-layout--fixed-header - = render 'partials/topbar' - %main.mdl-layout__content.layout__main-content - = yield - = render 'partials/flash' - - %script{src: '//cdn.ckeditor.com/4.7.3/standard-all/ckeditor.js', type: 'text/javascript'} - = javascript_include_tag :application, {'data-turbolinks-eval': false, 'data-turbolinks-track': 'reload'} - = javascript_pack_tag 'cortex-bundle', {'data-turbolinks-eval': false, 'data-turbolinks-track': 'reload'} - = render 'partials/trackers/google_analytics' if extra_config.google_analytics_id? - = render 'partials/trackers/qualtrics' if extra_config.qualtrics_id? diff --git a/app/views/layouts/cortex/application.html.haml b/app/views/layouts/cortex/application.html.haml new file mode 100644 index 000000000..9cf5906bb --- /dev/null +++ b/app/views/layouts/cortex/application.html.haml @@ -0,0 +1,30 @@ +!!! 5 +%html + %head + %meta{content: 'IE=edge', 'http-equiv' => 'X-UA-Compatible'} + %meta{charset: 'utf-8'} + %meta{name: 'viewport', content: 'width=device-width, initial-scale=1.0, minimum-scale=1.0'} + %title + = title + = favicon_link_tag 'cortex/favicon.ico' + = stylesheet_link_tag :application, {media: 'all'} + // = stylesheet_pack_tag 'cortex-bundle', {media: 'all'} # Enable once assets migrated + = stylesheet_link_tag 'https://fonts.googleapis.com/icon?family=Material+Icons' + = csrf_meta_tags + %body + = render 'cortex/partials/media_dialogs' + = render 'cortex/partials/cortex_redux_state' + #layout_wrapper{class: ('sidebar--collapsed' unless current_page? cortex.root_path)} + .layout__container.mdl-js-layout.mdl-layout--fixed-drawer + = render 'cortex/partials/sidebar' + .layout__topbar-content__container.mdl-layout--fixed-header + = render 'cortex/partials/topbar' + %main.mdl-layout__content.layout__main-content + = yield + = render 'cortex/partials/flash' + + %script{src: '//cdn.ckeditor.com/4.8.0/standard-all/ckeditor.js', type: 'text/javascript'} + = javascript_include_tag :application + = javascript_pack_tag 'cortex' + = render 'cortex/partials/trackers/google_analytics' if extra_config[:google_analytics_id?] + = render 'cortex/partials/trackers/qualtrics' if extra_config[:qualtrics_id?] diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/cortex/devise.html.haml similarity index 59% rename from app/views/layouts/devise.html.haml rename to app/views/layouts/cortex/devise.html.haml index 826a6f796..20cb5e221 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/cortex/devise.html.haml @@ -5,15 +5,15 @@ %meta{charset: 'utf-8'} %meta{name: 'viewport', content: 'width=device-width, initial-scale=1.0, minimum-scale=1.0'} %title Cortex Administration | Login - = favicon_link_tag 'favicon.ico' - = stylesheet_link_tag :authentication, {media: 'all', 'data-turbolinks-track': 'reload'} + = favicon_link_tag 'cortex/favicon.ico' + = stylesheet_link_tag 'cortex/authentication', {media: 'all'} = csrf_meta_tags %body.mdl-base .mdl-layout.mdl-js-layout.authentication %main.mdl-layout__content.mdl-layout__content--authentication - = render 'partials/authentication/header' + = render 'cortex/partials/authentication/header' .mdl-grid.authentication__content = yield - = javascript_include_tag :authentication, {'data-turbolinks-eval': false, 'data-turbolinks-track': 'reload'} + = javascript_include_tag 'cortex/authentication' %span.flash--invert - = render 'partials/flash' + = render 'cortex/partials/flash' diff --git a/app/views/layouts/cortex/mailer.html.erb b/app/views/layouts/cortex/mailer.html.erb new file mode 100644 index 000000000..cbd34d2e9 --- /dev/null +++ b/app/views/layouts/cortex/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/cortex/mailer.text.erb similarity index 100% rename from app/views/layouts/mailer.text.erb rename to app/views/layouts/cortex/mailer.text.erb diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb deleted file mode 100644 index 991cf0ffa..000000000 --- a/app/views/layouts/mailer.html.erb +++ /dev/null @@ -1,5 +0,0 @@ - - - <%= yield %> - - diff --git a/app/views/partials/_environment_display.html.haml b/app/views/partials/_environment_display.html.haml deleted file mode 100644 index 1e5b1c200..000000000 --- a/app/views/partials/_environment_display.html.haml +++ /dev/null @@ -1,6 +0,0 @@ - -.mdl-navigation__link.nav__item.environment{class: "environment--#{environment}"} - .environment__full - = environment - .environment__abbreviated - = environment_abbreviated diff --git a/app/views/partials/_topbar.html.haml b/app/views/partials/_topbar.html.haml deleted file mode 100644 index fcaa0b2ed..000000000 --- a/app/views/partials/_topbar.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -%header.mdl-layout__header - .mdl-layout__header-row - = render 'partials/breadcrumb' - .mdl-layout-spacer - = render 'partials/user_control' diff --git a/bin/bundle b/bin/bundle deleted file mode 100755 index 66e9889e8..000000000 --- a/bin/bundle +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -load Gem.bin_path('bundler', 'bundle') diff --git a/bin/rails b/bin/rails index 073966023..066bbfdf9 100755 --- a/bin/rails +++ b/bin/rails @@ -1,4 +1,25 @@ #!/usr/bin/env ruby -APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' +# This command will automatically be run when you run "rails" with Rails gems +# installed from the root of your application. + +ENGINE_ROOT = File.expand_path('..', __dir__) +ENGINE_PATH = File.expand_path('../lib/cortex/engine', __dir__) +APP_PATH = File.expand_path('../spec/dummy/config/application', __dir__) + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) + +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" +require "active_record/railtie" +require "action_controller/railtie" +require "action_mailer/railtie" +require "action_view/railtie" +#require "active_storage/engine" +#require "action_cable/engine" +require "sprockets/railtie" +# require "rails/test_unit/railtie" +require 'rails/engine/commands' diff --git a/bin/rake b/bin/rake deleted file mode 100755 index 17240489f..000000000 --- a/bin/rake +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env ruby -require_relative '../config/boot' -require 'rake' -Rake.application.run diff --git a/bin/rspec b/bin/rspec deleted file mode 100644 index d738b23c0..000000000 --- a/bin/rspec +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true -# -# This file was generated by Bundler. -# -# The application 'rspec' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", - Pathname.new(__FILE__).realpath) - -require "rubygems" -require "bundler/setup" - -load Gem.bin_path("rspec-core", "rspec") diff --git a/bin/setup b/bin/setup deleted file mode 100755 index 78c4e861d..000000000 --- a/bin/setup +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env ruby -require 'pathname' -require 'fileutils' -include FileUtils - -# path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) - -def system!(*args) - system(*args) || abort("\n== Command #{args} failed ==") -end - -chdir APP_ROOT do - # This script is a starting point to setup your application. - # Add necessary setup steps to this file. - - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - # Install JavaScript dependencies if using Yarn - # system('bin/yarn') - - - # puts "\n== Copying sample files ==" - # unless File.exist?('config/database.yml') - # cp 'config/database.yml.sample', 'config/database.yml' - # end - - puts "\n== Preparing database ==" - system! 'bin/rails db:setup' - - puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' - - puts "\n== Restarting application server ==" - system! 'bin/rails restart' -end diff --git a/bin/update b/bin/update deleted file mode 100755 index a8e4462f2..000000000 --- a/bin/update +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env ruby -require 'pathname' -require 'fileutils' -include FileUtils - -# path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) - -def system!(*args) - system(*args) || abort("\n== Command #{args} failed ==") -end - -chdir APP_ROOT do - # This script is a way to update your development environment automatically. - # Add necessary update steps to this file. - - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - puts "\n== Updating database ==" - system! 'bin/rails db:migrate' - - puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' - - puts "\n== Restarting application server ==" - system! 'bin/rails restart' -end diff --git a/bin/webpack b/bin/webpack deleted file mode 100755 index 9d3800c74..000000000 --- a/bin/webpack +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true -# -# This file was generated by Bundler. -# -# The application 'webpack' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", - Pathname.new(__FILE__).realpath) - -require "rubygems" -require "bundler/setup" - -load Gem.bin_path("webpacker", "webpack") diff --git a/bin/webpack-dev-server b/bin/webpack-dev-server deleted file mode 100755 index cf701102a..000000000 --- a/bin/webpack-dev-server +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true -# -# This file was generated by Bundler. -# -# The application 'webpack-dev-server' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", - Pathname.new(__FILE__).realpath) - -require "rubygems" -require "bundler/setup" - -load Gem.bin_path("webpacker", "webpack-dev-server") diff --git a/bin/yarn b/bin/yarn deleted file mode 100644 index c2bacef83..000000000 --- a/bin/yarn +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env ruby -VENDOR_PATH = File.expand_path('..', __dir__) -Dir.chdir(VENDOR_PATH) do - begin - exec "yarnpkg #{ARGV.join(" ")}" - rescue Errno::ENOENT - $stderr.puts "Yarn executable was not detected in the system." - $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" - exit 1 - end -end diff --git a/config.ru b/config.ru deleted file mode 100644 index f2ed3dc29..000000000 --- a/config.ru +++ /dev/null @@ -1,7 +0,0 @@ -# This file is used by Rack-based servers to start the application. - -require_relative 'config/environment' - -ApolloTracing.start_proxy(Cortex.apollo_engine_proxy_config.to_json) - -run Rails.application diff --git a/config/apollo_engine_proxy.yml b/config/apollo_engine_proxy.yml deleted file mode 100644 index 636b985c5..000000000 --- a/config/apollo_engine_proxy.yml +++ /dev/null @@ -1,23 +0,0 @@ -defaults: &defaults - apiKey: <%= ENV['APOLLO_ENGINE_PROXY_API_KEY'] %> - logging: - level: <%= ENV['APOLLO_ENGINE_PROXY_LOGGING_LEVEL'] %> - origins: - - http: - url: <%= ENV['APOLLO_ENGINE_PROXY_ORIGIN_HTTP_URL'] %> - frontends: - - host: <%= ENV['APOLLO_ENGINE_PROXY_FRONTEND_HOST'] %> - port: <%= ENV['APOLLO_ENGINE_PROXY_FRONTEND_PORT'] %> - endpoint: <%= ENV['APOLLO_ENGINE_PROXY_FRONTEND_ENDPOINT'] %> - -development: - <<: *defaults - -test: - <<: *defaults - -staging: - <<: *defaults - -production: - <<: *defaults diff --git a/config/application.rb b/config/application.rb deleted file mode 100644 index 57786676b..000000000 --- a/config/application.rb +++ /dev/null @@ -1,35 +0,0 @@ -require_relative 'boot' - -require 'rails/all' -require 'elasticsearch/rails/instrumentation' - -Bundler.require(*Rails.groups) - -module Cortex - class Application < Rails::Application - config.load_defaults 5.1 - - config.i18n.enforce_available_locales = true - - config.eager_load_paths += %W(#{config.root}/lib #{config.root}/lib/helpers #{config.root}/lib/breadcrumbs) - - config.active_record.default_timezone = :utc - config.active_job.queue_adapter = :sidekiq - - # Insert Rack::CORS as the first middleware - config.middleware.insert_before 0, Rack::Cors do - allow do - origins *((Cortex.config.cors.allowed_origins || '*').split(',') + - [Cortex.config.cors.allowed_origins_regex || '']) - resource '*', - :headers => :any, - :methods => [:get, :post, :options, :put, :patch, :delete] - end - end - - # Generators should use UUID PKs by default - config.generators do |generator| - generator.orm :active_record, primary_key_type: :uuid - end - end -end diff --git a/config/boot.rb b/config/boot.rb deleted file mode 100644 index 2bd49b168..000000000 --- a/config/boot.rb +++ /dev/null @@ -1,11 +0,0 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -require 'bundler/setup' - -# Cache requires -begin - puts 'Initializing bootscale..' - require 'bootscale/setup' -rescue LoadError - puts 'Skipping bootscale initialization - not loaded.' -end diff --git a/config/cable.yml b/config/cable.yml deleted file mode 100644 index 0bbde6f74..000000000 --- a/config/cable.yml +++ /dev/null @@ -1,9 +0,0 @@ -development: - adapter: async - -test: - adapter: async - -production: - adapter: redis - url: redis://localhost:6379/1 diff --git a/config/database.yml b/config/database.yml deleted file mode 100644 index 6513d6cf3..000000000 --- a/config/database.yml +++ /dev/null @@ -1,22 +0,0 @@ -defaults: &defaults - adapter: postgresql - encoding: unicode - database: <%= ENV['DATABASE_NAME'] || 'cortex_dev' %> - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - username: <%= ENV['DATABASE_USERNAME'] || '' %> - password: <%= ENV['DATABASE_PASSWORD'] || '' %> - host: <%= ENV['DATABASE_HOST'] || '' %> - port: <%= ENV['DATABASE_PORT'] || '' %> - -test: - <<: *defaults - database: <%= ENV['TEST_DATABASE_NAME'] || 'cortex_test' %> - -development: - <<: *defaults - -staging: - <<: *defaults - -production: - <<: *defaults diff --git a/config/environment.rb b/config/environment.rb deleted file mode 100644 index 3bba91856..000000000 --- a/config/environment.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Load the Rails application. -require_relative 'application' - -# Initialize the Rails application. -Cortex::Application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb deleted file mode 100644 index cde7e7dc4..000000000 --- a/config/environments/development.rb +++ /dev/null @@ -1,74 +0,0 @@ -Cortex::Application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. - config.cache_classes = false - - # Do not eager load code on boot. - config.eager_load = false - - # Show full error reports. - config.consider_all_requests_local = true - - # Enable/disable caching. By default caching is disabled. - if Rails.root.join('tmp/caching-dev.txt').exist? - config.action_controller.perform_caching = true - - config.cache_store = :memory_store - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" - } - else - config.action_controller.perform_caching = false - - config.cache_store = :null_store - end - - # Print deprecation notices to the Rails logger. - config.active_support.deprecation = :log - - # Raise an error on page load if there are pending migrations - config.active_record.migration_error = :page_load - - # Debug mode disables concatenation and preprocessing of assets. - # This option may cause significant delays in view rendering with a large - # number of complex assets. - config.assets.debug = true - - # Suppress logger output for asset requests. - config.assets.quiet = true - - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. - config.file_watcher = ActiveSupport::EventedFileUpdateChecker - - if ENV['DEPLOYED'] - config.cache_store = :redis_store, ENV['CACHE_URL'] - else - config.cache_store = :redis_store, ENV['CACHE_URL'], { :namespace => ENV['REDIS_NAMESPACE'] || 'cortex_dev' } - end - - Sidekiq.configure_server do |config| - config.redis = { :namespace => ENV['REDIS_NAMESPACE'] || 'cortex_dev' } unless ENV['DEPLOYED'] - end - - Sidekiq.configure_client do |config| - config.redis = { :namespace => ENV['REDIS_NAMESPACE'] || 'cortex_dev' } unless ENV['DEPLOYED'] - end - - config.action_mailer.default_url_options = {:host => ENV['HOST']} - config.action_mailer.perform_caching = false - config.action_mailer.delivery_method = :smtp - config.action_mailer.smtp_settings = { - :authentication => :login, - :address => ENV['SMTP_ADDRESS'], - :port => ENV['SMTP_PORT'], - :domain => ENV['SMTP_SENDER_DOMAIN'], - :user_name => ENV['SMTP_USERNAME'], - :password => ENV['SMTP_PASSWORD'], - :enable_starttls_auto => ENV['SMTP_STARTTLS'] - } - ActionMailer::Base.default from: ENV['SMTP_SENDER_ADDRESS'] -end diff --git a/config/environments/production.rb b/config/environments/production.rb deleted file mode 100644 index 15f28feca..000000000 --- a/config/environments/production.rb +++ /dev/null @@ -1,103 +0,0 @@ -Cortex::Application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # Code is not reloaded between requests. - config.cache_classes = true - - # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both thread web servers - # and those relying on copy on write to perform better. - # Rake tasks automatically ignore this option for performance. - config.eager_load = true - - # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false - config.action_controller.perform_caching = true - - # Attempt to read encrypted secrets from `config/secrets.yml.enc`. - # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or - # `config/secrets.yml.key`. - config.read_encrypted_secrets = true - - # Disable serving static files from the `/public` folder by default since - # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - - # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier - # config.assets.css_compressor = :sass - - # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false - - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb - - # Enable serving of images, stylesheets, and JavaScripts from an asset server. - if ENV['ASSET_HOST_URL'] - config.action_controller.asset_host = ENV['ASSET_HOST_URL'] - end - - # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx - - # Mount Action Cable outside main process or domain - # config.action_cable.mount_path = nil - # config.action_cable.url = 'wss://example.com/cable' - # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] - - # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true - - # Use the lowest log level to ensure availability of diagnostic information - # when problems arise. - config.log_level = :debug - - # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] - - # Use a different cache store in production. - config.cache_store = :redis_store, ENV['CACHE_URL'] - - # Use a real queuing backend for Active Job (and separate queues per environment) - # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "cool_#{Rails.env}" - config.action_mailer.perform_caching = false - - # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation can not be found). - config.i18n.fallbacks = true - - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify - - # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new - - # Use a different logger for distributed setups. - # require 'syslog/logger' - # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - - if ENV["RAILS_LOG_TO_STDOUT"].present? - logger = ActiveSupport::Logger.new(STDOUT) - logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) - end - - # Do not dump schema after migrations. - config.active_record.dump_schema_after_migration = false - - config.action_mailer.raise_delivery_errors = true - config.action_mailer.default_url_options = {:host => ENV['HOST']} - config.action_mailer.delivery_method = :smtp - config.action_mailer.smtp_settings = { - :authentication => :login, - :address => ENV['SMTP_ADDRESS'], - :port => ENV['SMTP_PORT'], - :domain => ENV['SMTP_SENDER_DOMAIN'], - :user_name => ENV['SMTP_USERNAME'], - :password => ENV['SMTP_PASSWORD'], - :enable_starttls_auto => ENV['SMTP_STARTTLS'] - } - ActionMailer::Base.default from: ENV['SMTP_SENDER_ADDRESS'] -end diff --git a/config/environments/staging.rb b/config/environments/staging.rb deleted file mode 100644 index 1a6fd1dc3..000000000 --- a/config/environments/staging.rb +++ /dev/null @@ -1,103 +0,0 @@ -Cortex::Application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # Code is not reloaded between requests. - config.cache_classes = true - - # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both threaded web servers - # and those relying on copy on write to perform better. - # Rake tasks automatically ignore this option for performance. - config.eager_load = true - - # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false - config.action_controller.perform_caching = true - - # Attempt to read encrypted secrets from `config/secrets.yml.enc`. - # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or - # `config/secrets.yml.key`. - config.read_encrypted_secrets = true - - # Disable serving static files from the `/public` folder by default since - # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - - # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier - # config.assets.css_compressor = :sass - - # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false - - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb - - # Enable serving of images, stylesheets, and JavaScripts from an asset server. - if ENV['ASSET_HOST_URL'] - config.action_controller.asset_host = ENV['ASSET_HOST_URL'] - end - - # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX - - # Mount Action Cable outside main process or domain - # config.action_cable.mount_path = nil - # config.action_cable.url = 'wss://example.com/cable' - # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] - - # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true - - # Use the lowest log level to ensure availability of diagnostic information - # when problems arise. - config.log_level = :debug - - # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] - - # Use a different cache store in production. - config.cache_store = :redis_store, ENV['CACHE_URL'] - - # Use a real queuing backend for Active Job (and separate queues per environment) - # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "cool_#{Rails.env}" - config.action_mailer.perform_caching = false - - # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation cannot be found). - config.i18n.fallbacks = true - - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify - - # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new - - # Use a different logger for distributed setups. - # require 'syslog/logger' - # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - - if ENV["RAILS_LOG_TO_STDOUT"].present? - logger = ActiveSupport::Logger.new(STDOUT) - logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) - end - - # Do not dump schema after migrations. - config.active_record.dump_schema_after_migration = false - - config.action_mailer.raise_delivery_errors = true - config.action_mailer.default_url_options = {:host => ENV['HOST']} - config.action_mailer.delivery_method = :smtp - config.action_mailer.smtp_settings = { - :authentication => :login, - :address => ENV['SMTP_ADDRESS'], - :port => ENV['SMTP_PORT'], - :domain => ENV['SMTP_SENDER_DOMAIN'], - :user_name => ENV['SMTP_USERNAME'], - :password => ENV['SMTP_PASSWORD'], - :enable_starttls_auto => ENV['SMTP_STARTTLS'] - } - ActionMailer::Base.default from: ENV['SMTP_SENDER_ADDRESS'] -end diff --git a/config/environments/test.rb b/config/environments/test.rb deleted file mode 100644 index d266f681a..000000000 --- a/config/environments/test.rb +++ /dev/null @@ -1,65 +0,0 @@ -Cortex::Application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # The test environment is used exclusively to run your application's - # test suite. You never need to work with it otherwise. Remember that - # your test database is "scratch space" for the test suite and is wiped - # and recreated between test runs. Don't rely on the data there! - config.cache_classes = true - - # Do not eager load code on boot. This avoids loading your whole application - # just for the purpose of running a single test. If you are using a tool that - # preloads Rails for running tests, you may have to set it to true. - config.eager_load = false - - # Configure public file server for tests with Cache-Control for performance. - config.public_file_server.enabled = true - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" - } - - # Show full error reports and disable caching. - config.consider_all_requests_local = true - config.action_controller.perform_caching = false - - # Raise exceptions instead of rendering exception templates. - config.action_dispatch.show_exceptions = false - - # Disable request forgery protection in test environment. - config.action_controller.allow_forgery_protection = false - config.action_mailer.perform_caching = false - - # Tell Action Mailer not to deliver emails to the real world. - # The :test delivery method accumulates sent emails in the - # ActionMailer::Base.deliveries array. - config.action_mailer.delivery_method = :test - - # Print deprecation notices to the stderr. - config.active_support.deprecation = :stderr - - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true - - config.cache_store = :redis_store, ENV['CACHE_URL'], { :namespace => 'cortex_test' } - - Sidekiq.configure_server do |config| - config.redis = { :namespace => 'cortex_test' } - end - - Sidekiq.configure_client do |config| - config.redis = { :namespace => 'cortex_test' } - end - - config.action_mailer.default_url_options = {:host => ENV['HOST']} - config.action_mailer.delivery_method = :test - config.action_mailer.smtp_settings = { - :authentication => :login, - :address => ENV['SMTP_ADDRESS'], - :port => ENV['SMTP_PORT'], - :domain => ENV['SMTP_SENDER_DOMAIN'], - :user_name => ENV['SMTP_USERNAME'], - :password => ENV['SMTP_PASSWORD'], - :enable_starttls_auto => ENV['SMTP_STARTTLS'] - } - ActionMailer::Base.default from: ENV['SMTP_SENDER_ADDRESS'] -end diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb deleted file mode 100644 index 51639b67a..000000000 --- a/config/initializers/application_controller_renderer.rb +++ /dev/null @@ -1,6 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# ApplicationController.renderer.defaults.merge!( -# http_host: 'example.org', -# https: false -# ) diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb deleted file mode 100644 index f72d27565..000000000 --- a/config/initializers/assets.rb +++ /dev/null @@ -1,28 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' - -# Add additional assets to the asset load path -# Rails.application.config.assets.paths << Emoji.images_path -# Add Yarn node_modules folder to the asset load path. -Rails.application.config.assets.paths << Rails.root.join('node_modules') - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in the app/assets -# folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) -Rails.application.config.assets.precompile += %w( authentication.scss ) -Rails.application.config.assets.precompile += %w( authentication.js ) - -Rails.application.config.assets.precompile += %w( ckeditor/* ) -# Add client/assets/ folders to asset pipeline's search path. -# If you do not want to move existing images and fonts from your Rails app -# you could also consider creating symlinks there that point to the original -# rails directories. In that case, you would not add these paths here. -# If you have a different server bundle file than your client bundle, you'll -# need to add it here, like this: -# Rails.application.config.assets.precompile += %w( server-bundle.js ) - -# Add folder with webpack generated assets to assets.paths -Rails.application.config.assets.paths << Rails.root.join("app", "assets", "webpack") diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb deleted file mode 100644 index 59385cdf3..000000000 --- a/config/initializers/backtrace_silencers.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. -# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } - -# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. -# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb deleted file mode 100644 index 5a6a32d37..000000000 --- a/config/initializers/cookies_serializer.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Specify a serializer for the signed and encrypted cookie jars. -# Valid options are :json, :marshal, and :hybrid. -Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index f82b79e9b..37033f8ca 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -24,6 +24,12 @@ config.sign_out_via = :delete config.warden do |manager| - manager.failure_app = AuthenticationFailure + manager.failure_app = Cortex::AuthenticationFailure + end + + # Engine-specific config + config.router_name = :cortex + Devise.setup do |config| + config.parent_controller = 'Cortex::ApplicationController' end end diff --git a/config/initializers/elasticsearch.rb b/config/initializers/elasticsearch.rb index 454170184..892506b32 100644 --- a/config/initializers/elasticsearch.rb +++ b/config/initializers/elasticsearch.rb @@ -1 +1,3 @@ +require 'elasticsearch/model' + Elasticsearch::Model.client = Elasticsearch::Client.new url: ENV['ELASTICSEARCH_ADDRESS'] diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb index 2efda798e..31fd30624 100644 --- a/config/initializers/flipper.rb +++ b/config/initializers/flipper.rb @@ -1,3 +1,5 @@ +require 'flipper/middleware/memoizer' + Flipper.configure do |config| config.default do adapter = Flipper::Adapters::ActiveRecord.new @@ -5,7 +7,7 @@ end end -Cortex::Application.config.middleware.use Flipper::Middleware::Memoizer +Cortex::Engine.config.middleware.use Flipper::Middleware::Memoizer Flipper.register(:internal) { |request| request.internal? } Flipper.register(:authenticated) { |request| request.session[:current_user].present? && request.session[:current_user][:authenticated] } diff --git a/config/initializers/graphiql.rb b/config/initializers/graphiql.rb index 799128790..a90b9be46 100644 --- a/config/initializers/graphiql.rb +++ b/config/initializers/graphiql.rb @@ -1,3 +1,5 @@ +require 'graphiql/rails' + GraphiQL::Rails.config.initial_query = <<~HEREDOC # Welcome to Cortex CMS's GraphiQL IDE # diff --git a/config/initializers/load_config.rb b/config/initializers/load_config.rb index bdd9a4bf9..1dddb32e1 100644 --- a/config/initializers/load_config.rb +++ b/config/initializers/load_config.rb @@ -1,11 +1,16 @@ module Cortex def self.config - @config ||= Hashr.new(Rails.application.config_for(:config)) + @config ||= self.load_yaml("#{Cortex::Engine.root}/config/config.yml") end - def self.apollo_engine_proxy_config - @apollo_engine_proxy_config ||= Rails.application.config_for(:apollo_engine_proxy) + def self.seed_data + @seed_data ||= self.load_yaml("#{Cortex::Engine.root}/db/seeds.yml") end -end -::SeedData = Hashr.new(YAML.load_file("#{Rails.root}/db/seeds.yml")[Rails.env]) + private + + def self.load_yaml(file) + # interpolate file with ERB to allow templating (<%= ENV['...'] %>) + YAML.load(ERB.new(File.new(file).read).result)[Rails.env].deep_symbolize_keys + end +end diff --git a/config/initializers/plugins.rb b/config/initializers/plugins.rb index 92ef61b7a..efcb5c1dd 100644 --- a/config/initializers/plugins.rb +++ b/config/initializers/plugins.rb @@ -1,16 +1,16 @@ module Cortex def self.plugins - Cortex::Plugins.constants.map(&Cortex::Plugins.method(:const_get)) + @plugins ||= Cortex::Plugins.constants.map(&Cortex::Plugins.method(:const_get)) end def self.plugin_library_names - plugins.map do |plugin| + @plugin_library_names ||= @plugins.map do |plugin| plugin.to_s.parameterize end end def self.plugin_paths - plugins.map do |plugin| + @plugin_paths ||= @plugins.map do |plugin| plugin::Engine.root end end diff --git a/config/initializers/react_on_rails.rb b/config/initializers/react_on_rails.rb index 2b77a9f75..51bfd101b 100644 --- a/config/initializers/react_on_rails.rb +++ b/config/initializers/react_on_rails.rb @@ -1,52 +1,15 @@ +ActiveSupport.on_load(:action_view) do + include ReactOnRailsHelper +end + module RenderingExtension - def self.custom_context(view_context) + def self.custom_context(_view_context) { - envUrlPrefix: !(Rails.env == 'production' || Rails.env == 'failover') ? 'wwwtest' : '', environment: Rails.env } end end -# See docs/basics/configuration.md for many more options - ReactOnRails.configure do |config| - # This configures the script to run to build the production assets by webpack. Set this to nil - # if you don't want react_on_rails building this file for you. - config.build_production_command = "RAILS_ENV=production bin/webpack" - - ################################################################################ - ################################################################################ - # TEST CONFIGURATION OPTIONS - # Below options are used with the use of this test helper: - # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) - ################################################################################ - - # If you are using this in your spec_helper.rb (or rails_helper.rb): - # - # ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config) - # - # with rspec then this controls what yarn command is run - # to automatically refresh your webpack assets on every test run. - # - config.build_test_command = "RAILS_ENV=test bin/webpack" - - ################################################################################ - ################################################################################ - # SERVER RENDERING OPTIONS - ################################################################################ - # This is the file used for server rendering of React when using `(prerender: true)` - # If you are never using server rendering, you should set this to "". - # Note, there is only one server bundle, unlike JavaScript where you want to minimize the size - # of the JS sent to the client. For the server rendering, React on Rails creates a pool of - # JavaScript execution instances which should handle any component requested. - # - # While you may configure this to be the same as your client bundle file, this file is typically - # different. You should have ONE server bundle which can create all of your server rendered - # React components. - # - config.server_bundle_js_file = "hello-world-bundle.js" - - config.prerender = !Rails.env.development? - config.rendering_extension = RenderingExtension end diff --git a/config/initializers/rolify.rb b/config/initializers/rolify.rb index d7c0c90b6..72e9b99cf 100644 --- a/config/initializers/rolify.rb +++ b/config/initializers/rolify.rb @@ -1,3 +1,5 @@ +require 'rolify' + Rolify.configure do |config| # By default ORM adapter is ActiveRecord. uncomment to use mongoid # config.use_mongoid diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb deleted file mode 100644 index e9fb7c8b6..000000000 --- a/config/initializers/secret_token.rb +++ /dev/null @@ -1,12 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Your secret key is used for verifying the integrity of signed cookies. -# If you change this key, all old signed cookies will become invalid! - -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -# You can use `rake secret` to generate a secure secret key. - -# Make sure your secret_key_base is kept private -# if you're sharing your code publicly. -Cortex::Application.config.secret_key_base = ENV['APP_SECRET'] diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb deleted file mode 100644 index b65a07019..000000000 --- a/config/initializers/sentry.rb +++ /dev/null @@ -1,6 +0,0 @@ -if defined? Raven - Raven.configure do |config| - config.dsn = ENV['SENTRY_RAVEN_DSN'] - config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s) - end -end diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb deleted file mode 100644 index 22e807c7c..000000000 --- a/config/initializers/session_store.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Be sure to restart your server when you modify this file. - -if ENV['DEPLOYED'] || Rails.env.production? - Rails.application.config.session_store :redis_store, servers: ENV['SESSION_STORE_URL'] -else - Rails.application.config.session_store :redis_store, servers: ENV['SESSION_STORE_URL'], namespace: ENV['REDIS_NAMESPACE'] || 'cortex_dev' -end diff --git a/config/initializers/tag_parser.rb b/config/initializers/tag_parser.rb index ff52ed36d..8c843311b 100644 --- a/config/initializers/tag_parser.rb +++ b/config/initializers/tag_parser.rb @@ -1,5 +1,5 @@ module Cortex def self.available_tag_parsers - [:media] + @available_tag_parsers ||= [:media] end end diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb deleted file mode 100644 index cf733efd1..000000000 --- a/config/initializers/wrap_parameters.rb +++ /dev/null @@ -1,14 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# This file contains settings for ActionController::ParamsWrapper which -# is enabled by default. - -# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. -ActiveSupport.on_load(:action_controller) do - wrap_parameters format: [:json] -end - -# To enable root element in JSON for ActiveRecord objects. -# ActiveSupport.on_load(:active_record) do -# self.include_root_in_json = true -# end diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index 6cd4cd277..0b8f13027 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -3,49 +3,54 @@ en: devise: confirmations: - confirmed: "Your account was successfully confirmed." - send_instructions: "You will receive an email with instructions about how to confirm your account in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions about how to confirm your account in a few minutes." + confirmed: "Your email address has been successfully confirmed." + send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." + send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." failure: already_authenticated: "You are already signed in." inactive: "Your account is not activated yet." - invalid: "Invalid email or password." + invalid: "Invalid %{authentication_keys} or password." locked: "Your account is locked." - last_attempt: "You have one more attempt before your account will be locked." - not_found_in_database: "Invalid email or password." + last_attempt: "You have one more attempt before your account is locked." + not_found_in_database: "Invalid %{authentication_keys} or password." timeout: "Your session expired. Please sign in again to continue." unauthenticated: "You need to sign in or sign up before continuing." - unconfirmed: "You have to confirm your account before continuing." + unconfirmed: "You have to confirm your email address before continuing." mailer: confirmation_instructions: subject: "Confirmation instructions" reset_password_instructions: subject: "Reset password instructions" unlock_instructions: - subject: "Unlock Instructions" + subject: "Unlock instructions" + email_changed: + subject: "Email Changed" + password_change: + subject: "Password Changed" omniauth_callbacks: failure: "Could not authenticate you from %{kind} because \"%{reason}\"." success: "Successfully authenticated from %{kind} account." passwords: no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." - send_instructions: "You will receive an email with instructions about how to reset your password in a few minutes." + send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." - updated: "Your password was changed successfully. You are now signed in." - updated_not_active: "Your password was changed successfully." + updated: "Your password has been changed successfully. You are now signed in." + updated_not_active: "Your password has been changed successfully." registrations: - destroyed: "Bye! Your account was successfully cancelled. We hope to see you again soon." + destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." signed_up: "Welcome! You have signed up successfully." signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." - signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please open the link to activate your account." - update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and click on the confirm link to finalize confirming your new email address." - updated: "You updated your account successfully." + signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." + update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." + updated: "Your account has been updated successfully." sessions: signed_in: "Signed in successfully." signed_out: "Signed out successfully." + already_signed_out: "Signed out successfully." unlocks: - send_instructions: "You will receive an email with instructions about how to unlock your account in a few minutes." - send_paranoid_instructions: "If your account exists, you will receive an email with instructions about how to unlock it in a few minutes." + send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." + send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." unlocked: "Your account has been unlocked successfully. Please sign in to continue." errors: messages: diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml deleted file mode 100644 index c44bdb414..000000000 --- a/config/locales/doorkeeper.en.yml +++ /dev/null @@ -1,74 +0,0 @@ -en: - activerecord: - errors: - models: - application: - attributes: - redirect_uri: - fragment_present: 'cannot contain a fragment.' - has_query_parameter: 'cannot contain a query parameter.' - invalid_uri: 'must be a valid URI.' - relative_uri: 'must be an absolute URI.' - mongoid: - errors: - models: - application: - attributes: - redirect_uri: - fragment_present: 'cannot contain a fragment.' - has_query_parameter: 'cannot contain a query parameter.' - invalid_uri: 'must be a valid URI.' - relative_uri: 'must be an absolute URI.' - mongo_mapper: - errors: - models: - application: - attributes: - redirect_uri: - fragment_present: 'cannot contain a fragment.' - has_query_parameter: 'cannot contain a query parameter.' - invalid_uri: 'must be a valid URI.' - relative_uri: 'must be an absolute URI.' - doorkeeper: - errors: - messages: - # Common error messages - invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.' - invalid_redirect_uri: 'The redirect uri included is not valid.' - unauthorized_client: 'The client is not authorized to perform this request using this method.' - access_denied: 'The resource owner or authorization server denied the request.' - invalid_scope: 'The requested scope is invalid, unknown, or malformed.' - server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' - temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.' - - #configuration error messages - credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.' - resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.' - - # Access grant errors - unsupported_response_type: 'The authorization server does not support this response type.' - - # Access token errors - invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.' - invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.' - unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.' - - # Password Access token errors - invalid_resource_owner: 'The provided resource owner credentials are not valid, or resource owner cannot be found' - - invalid_token: - revoked: "The access token was revoked" - expired: "The access token expired" - unknown: "The access token is invalid" - - flash: - applications: - create: - notice: 'Application created.' - destroy: - notice: 'Application deleted.' - update: - notice: 'Application updated.' - authorized_applications: - destroy: - notice: 'Application revoked.' diff --git a/config/locales/en.yml b/config/locales/en.yml deleted file mode 100644 index 00a339557..000000000 --- a/config/locales/en.yml +++ /dev/null @@ -1,8 +0,0 @@ -en: - doorkeeper: - scopes: - public: View your public information - view:tenants: View tenants you control - modify:tenants: Make changes to, create and delete, tenants you control - view:users: View users you control - modify:users: Make changes to, create and delete, users you control diff --git a/config/newrelic.yml b/config/newrelic.yml deleted file mode 100644 index 3a757cf10..000000000 --- a/config/newrelic.yml +++ /dev/null @@ -1,224 +0,0 @@ -# -# This file configures the New Relic Agent. New Relic monitors Ruby, Java, -# .NET, PHP, Python and Node applications with deep visibility and low -# overhead. For more information, visit www.newrelic.com. -# -# Generated October 14, 2014 -# -# This configuration file is custom generated for Content Enablement - - -# Here are the settings that are common to all environments -common: &default_settings - # ============================== LICENSE KEY =============================== - - # You must specify the license key associated with your New Relic - # account. This key binds your Agent's data to your account in the - # New Relic service. - license_key: <%= ENV['NEW_RELIC_LICENSE_KEY'] %> - - # Agent Enabled (Ruby/Rails Only) - # Use this setting to force the agent to run or not run. - # Default is 'auto' which means the agent will install and run only - # if a valid dispatcher such as Mongrel is running. This prevents - # it from running with Rake or the console. Set to false to - # completely turn the agent off regardless of the other settings. - # Valid values are true, false and auto. - # - # agent_enabled: auto - - # Application Name Set this to be the name of your application as - # you'd like it show up in New Relic. The service will then auto-map - # instances of your application into an "application" on your - # dashboard page. If you want to map this instance into multiple - # apps, like "AJAX Requests" and "All UI" then specify a semicolon - # separated list of up to three distinct names, or a yaml list. - # Defaults to the capitalized RAILS_ENV or RACK_ENV (i.e., - # Production, Staging, etc) - # - # Example: - # - # app_name: - # - Ajax Service - # - All Services - # - # Caution: If you change this name, a new application will appear in the New - # Relic user interface with the new name, and data will stop reporting to the - # app with the old name. - # - # See https://newrelic.com/docs/site/renaming-applications for more details - # on renaming your New Relic applications. - # - app_name: Cortex - - # When "true", the agent collects performance data about your - # application and reports this data to the New Relic service at - # newrelic.com. This global switch is normally overridden for each - # environment below. (formerly called 'enabled') - monitor_mode: true - - # Developer mode should be off in every environment but - # development as it has very high overhead in memory. - developer_mode: false - - # The newrelic agent generates its own log file to keep its logging - # information separate from that of your application. Specify its - # log level here. - log_level: info - - # Optionally set the path to the log file This is expanded from the - # root directory (may be relative or absolute, e.g. 'log/' or - # '/var/log/') The agent will attempt to create this directory if it - # does not exist. - # log_file_path: 'log' - - # Optionally set the name of the log file, defaults to 'newrelic_agent.log' - # log_file_name: 'newrelic_agent.log' - - # The newrelic agent communicates with the service via https by default. This - # prevents eavesdropping on the performance metrics transmitted by the agent. - # The encryption required by SSL introduces a nominal amount of CPU overhead, - # which is performed asynchronously in a background thread. If you'd prefer - # to send your metrics over http uncomment the following line. - # ssl: false - - #============================== Browser Monitoring =============================== - # New Relic Real User Monitoring gives you insight into the performance real users are - # experiencing with your website. This is accomplished by measuring the time it takes for - # your users' browsers to download and render your web pages by injecting a small amount - # of JavaScript code into the header and footer of each page. - browser_monitoring: - # By default the agent automatically injects the monitoring JavaScript - # into web pages. Set this attribute to false to turn off this behavior. - auto_instrument: true - - # Proxy settings for connecting to the New Relic server. - # - # If a proxy is used, the host setting is required. Other settings - # are optional. Default port is 8080. - # - # proxy_host: hostname - # proxy_port: 8080 - # proxy_user: - # proxy_pass: - - # The agent can optionally log all data it sends to New Relic servers to a - # separate log file for human inspection and auditing purposes. To enable this - # feature, change 'enabled' below to true. - # See: https://newrelic.com/docs/ruby/audit-log - audit_log: - enabled: false - - # Tells transaction tracer and error collector (when enabled) - # whether or not to capture HTTP params. When true, frameworks can - # exclude HTTP parameters from being captured. - # Rails: the RoR filter_parameter_logging excludes parameters - # Java: create a config setting called "ignored_params" and set it to - # a comma separated list of HTTP parameter names. - # ex: ignored_params: credit_card, ssn, password - capture_params: true - - # Transaction tracer captures deep information about slow - # transactions and sends this to the New Relic service once a - # minute. Included in the transaction is the exact call sequence of - # the transactions including any SQL statements issued. - transaction_tracer: - - # Transaction tracer is enabled by default. Set this to false to - # turn it off. This feature is only available at the Professional - # and above product levels. - enabled: true - - # Threshold in seconds for when to collect a transaction - # trace. When the response time of a controller action exceeds - # this threshold, a transaction trace will be recorded and sent to - # New Relic. Valid values are any float value, or (default) "apdex_f", - # which will use the threshold for an dissatisfying Apdex - # controller action - four times the Apdex T value. - transaction_threshold: apdex_f - - # When transaction tracer is on, SQL statements can optionally be - # recorded. The recorder has three modes, "off" which sends no - # SQL, "raw" which sends the SQL statement in its original form, - # and "obfuscated", which strips out numeric and string literals. - record_sql: obfuscated - - # Threshold in seconds for when to collect stack trace for a SQL - # call. In other words, when SQL statements exceed this threshold, - # then capture and send to New Relic the current stack trace. This is - # helpful for pinpointing where long SQL calls originate from. - stack_trace_threshold: 0.500 - - # Determines whether the agent will capture query plans for slow - # SQL queries. Only supported in mysql and postgres. Should be - # set to false when using other adapters. - # explain_enabled: true - - # Threshold for query execution time below which query plans will - # not be captured. Relevant only when `explain_enabled` is true. - # explain_threshold: 0.5 - - # Error collector captures information about uncaught exceptions and - # sends them to New Relic for viewing - error_collector: - - # Error collector is enabled by default. Set this to false to turn - # it off. This feature is only available at the Professional and above - # product levels. - enabled: true - - # To stop specific errors from reporting to New Relic, set this property - # to comma-separated values. Default is to ignore routing errors, - # which are how 404's get triggered. - ignore_errors: "ActionController::RoutingError,Sinatra::NotFound" - - # If you're interested in capturing memcache keys as though they - # were SQL uncomment this flag. Note that this does increase - # overhead slightly on every memcached call, and can have security - # implications if your memcached keys are sensitive - # capture_memcache_keys: true - -# Application Environments -# ------------------------------------------ -# Environment-specific settings are in this section. -# For Rails applications, RAILS_ENV is used to determine the environment. -# For Java applications, pass -Dnewrelic.environment to set -# the environment. - -# NOTE if your application has other named environments, you should -# provide newrelic configuration settings for these environments here. - -development: - <<: *default_settings - # Turn on communication to New Relic service in development mode - monitor_mode: true - app_name: Cortex Admin (Development) - - # Rails Only - when running in Developer Mode, the New Relic Agent will - # present performance information on the last 100 transactions you have - # executed since starting the mongrel. - # NOTE: There is substantial overhead when running in developer mode. - # Do not use for production or load testing. - developer_mode: true - -test: - <<: *default_settings - # It almost never makes sense to turn on the agent when running - # unit, functional or integration tests or the like. - monitor_mode: false - -# Turn on the agent in production for 24x7 monitoring. NewRelic -# testing shows an average performance impact of < 5 ms per -# transaction, you can leave this on all the time without -# incurring any user-visible performance degradation. -production: - <<: *default_settings - monitor_mode: true - -# Many applications have a staging environment which behaves -# identically to production. Support for that environment is provided -# here. By default, the staging environment has the agent turned on. -staging: - <<: *default_settings - monitor_mode: true - app_name: Cortex Admin (Staging) diff --git a/config/puma.rb b/config/puma.rb deleted file mode 100644 index fa11fb8fd..000000000 --- a/config/puma.rb +++ /dev/null @@ -1,47 +0,0 @@ -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum, this matches the default thread size of Active Record. -# -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i -threads threads_count, threads_count - -# Specifies the `port` that Puma will listen on to receive requests, default is 3000. -# -port ENV.fetch("PORT") { 3000 } - -# Specifies the `environment` that Puma will run in. -# -environment ENV.fetch("RAILS_ENV") { "development" } - -# Specifies the number of `workers` to boot in clustered mode. -# Workers are forked webserver processes. If using threads and workers together -# the concurrency of the application would be max `threads` * `workers`. -# Workers do not work on JRuby or Windows (both of which do not support -# processes). -# -workers ENV.fetch("WEB_CONCURRENCY") { 2 } - -# Use the `preload_app!` method when specifying a `workers` number. -# This directive tells Puma to first boot the application and load code -# before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. If you use this option -# you need to make sure to reconnect any threads in the `on_worker_boot` -# block. -# -preload_app! - -# The code in the `on_worker_boot` will be called if you are using -# clustered mode by specifying a number of `workers`. After each worker -# process is booted this block will be run, if you are using `preload_app!` -# option you will want to use this block to reconnect to any threads -# or connections that may have been created at application boot, Ruby -# cannot share connections between processes. -# -on_worker_boot do - ActiveRecord::Base.establish_connection if defined?(ActiveRecord) -end - -# Allow puma to be restarted by `rails restart` command. -plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb index 969bc536d..75d4ca55d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,7 @@ require 'sidekiq/web' +require 'flipper/ui' -Cortex::Application.routes.draw do +Cortex::Engine.routes.draw do # API - TODO: Authorize GraphQL + GraphiQL mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: ENV['GRAPHQL_URL'], as: 'graphiql' scope '/graphql' do @@ -30,7 +31,7 @@ end # Authentication - devise_for :users, controllers: {sessions: 'authentication/sessions', passwords: 'authentication/passwords'} + devise_for :users, controllers: {sessions: 'cortex/authentication/sessions', passwords: 'cortex/authentication/passwords'}, class_name: 'Cortex::User', module: :devise # Sidekiq Admin TODO: this needs to be updated with new role system #authenticate :user, lambda { |u| u.is_admin? } do @@ -39,7 +40,7 @@ # Flipper TODO: this needs to be updated with new role system #authenticated :user, lambda {|u| u.is_admin? } do - mount Flipper::UI.app(Flipper) => '/flipper' + mount Flipper::UI.app(Flipper) => '/flipper' #end mount JasmineRails::Engine => '/specs' if defined?(JasmineRails) diff --git a/config/secrets.yml b/config/secrets.yml deleted file mode 100644 index a06374a92..000000000 --- a/config/secrets.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Your secret key is used for verifying the integrity of signed cookies. -# If you change this key, all old signed cookies will become invalid! - -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -# You can use `rake secret` to generate a secure secret key. - -# Make sure the secrets in this file are kept private -# if you're sharing your code publicly. - -development: - secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> - -test: - secret_key_base: b32cc035d1b68446fcacf8b7106e92dcfaf538a07865e61b795f912cfa6c9a6e2c87f7f6cc5924b5ea3793c3baad82284a91418ed2e4db0e263c616304cab463 - -# Do not keep production secrets in the repository, -# instead read values from the environment. -production: - secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/config/sidekiq.yml b/config/sidekiq.yml deleted file mode 100644 index 3d98abaaa..000000000 --- a/config/sidekiq.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -:concurrency: 5 -:queues: - - default - - mailers -development: - :verbose: true -production: - :concurrency: 10 diff --git a/config/webpack/development.js b/config/webpack/development.js deleted file mode 100644 index 81269f651..000000000 --- a/config/webpack/development.js +++ /dev/null @@ -1,3 +0,0 @@ -const environment = require('./environment') - -module.exports = environment.toWebpackConfig() diff --git a/config/webpack/environment.js b/config/webpack/environment.js deleted file mode 100644 index d16d9af74..000000000 --- a/config/webpack/environment.js +++ /dev/null @@ -1,3 +0,0 @@ -const { environment } = require('@rails/webpacker') - -module.exports = environment diff --git a/config/webpack/production.js b/config/webpack/production.js deleted file mode 100644 index 81269f651..000000000 --- a/config/webpack/production.js +++ /dev/null @@ -1,3 +0,0 @@ -const environment = require('./environment') - -module.exports = environment.toWebpackConfig() diff --git a/config/webpack/test.js b/config/webpack/test.js deleted file mode 100644 index 81269f651..000000000 --- a/config/webpack/test.js +++ /dev/null @@ -1,3 +0,0 @@ -const environment = require('./environment') - -module.exports = environment.toWebpackConfig() diff --git a/config/webpacker.yml b/config/webpacker.yml deleted file mode 100644 index db88261af..000000000 --- a/config/webpacker.yml +++ /dev/null @@ -1,66 +0,0 @@ -# Note: You must restart bin/webpack-dev-server for changes to take effect - -default: &default - source_path: app/javascript - source_entry_path: packs - public_output_path: packs - cache_path: tmp/cache/webpacker - - # Additional paths webpack should lookup modules - # ['app/assets', 'engine/foo/app/assets'] - resolved_paths: [] - - # Reload manifest.json on all requests so we reload latest compiled packs - cache_manifest: false - - extensions: - - .coffee - - .erb - - .js - - .jsx - - .ts - - .vue - - .sass - - .scss - - .css - - .png - - .svg - - .gif - - .jpeg - - .jpg - -development: - <<: *default - compile: true - - # Reference: https://webpack.js.org/configuration/dev-server/ - dev_server: - https: false - host: localhost - port: 3035 - public: localhost:3035 - hmr: false - # Inline should be set to true if using HMR - inline: true - overlay: true - disable_host_check: true - use_local_ip: false - -test: - <<: *default - compile: true - - # Compile test packs to a separate directory - public_output_path: packs-test - -production: &production - <<: *default - - # Production depends on precompilation of packs prior to booting for performance. - compile: false - - # Cache manifest.json for performance - cache_manifest: true - -staging: - <<: *production diff --git a/cortex.gemspec b/cortex.gemspec new file mode 100644 index 000000000..27b332d2c --- /dev/null +++ b/cortex.gemspec @@ -0,0 +1,84 @@ +$:.push File.expand_path("lib", __dir__) + +# Maintain your gem's version: +require 'cortex/version' + +# Describe your s.add_dependency and declare its dependencies: +Gem::Specification.new do |s| + s.name = 'cortex' + s.version = Cortex::VERSION + s.authors = ['CareerBuilder Employer Site & Content Products'] + s.email = 'toastercup@gmail.com' + s.homepage = 'https://github.com/cbdr/cortex' + s.summary = 'An API-driven multitenant identity, dynamic content distribution/management and reporting platform powered by Rails, GraphQL and ElasticSearch' + s.license = 'Apache-2.0' + + s.files = `git ls-files -z`.split("\x0").reject do |f| + f.match(%r{^(test|spec|features)/}) + end + s.test_files = Dir["spec/**/*"] + s.require_paths = ["lib"] + + # Rails + s.add_dependency 'rails', '~> 5.1.4' + + # API + s.add_dependency 'graphql', '~> 1.7.5' + s.add_dependency 'graphiql-rails', '~> 1.4.7' + + # Cortex-specific + s.add_dependency 'cortex-exceptions', '= 0.0.4' + #s.add_dependency 'cortex-plugins-core', path: '/Users/atharp/Repos/cortex-plugins-core' # TODO: this is an awkward dependency, but necessary until we clean up hardcoded references to Plugins::Core + + # Service Layer + s.add_dependency 'dry-types', '~> 0.12.2' + s.add_dependency 'dry-struct', '~> 0.4.0' + s.add_dependency 'dry-transaction', '~> 0.10.2' + + # Authentication + s.add_dependency 'devise', '~> 4.3.0' + + # Authorization + s.add_dependency 'rolify', '~> 5.1.0' + s.add_dependency 'pundit', '~> 1.1.0' + + # Data + s.add_dependency 'awesome_nested_set', '~> 3.1.3' + s.add_dependency 'elasticsearch-model', '~> 5.0' + s.add_dependency 'elasticsearch-rails', '~> 5.0' + s.add_dependency 'elasticsearch-dsl', '~> 0.1' + s.add_dependency 'paranoia', '~> 2.4' + s.add_dependency 'pomona', '~> 0.7' + s.add_dependency 'transitions', '~> 1.2' + + # Middleware + s.add_dependency 'rack-cors', '~> 1.0.2' + + # Utility + s.add_dependency 'hashie', '~> 3.5.6' + s.add_dependency 'mimemagic', '~> 0.3.2' + s.add_dependency 'addressable', '~> 2.5.2' + s.add_dependency 'json' + s.add_dependency 'nokogiri' + + # View + s.add_dependency 'haml', '~> 5.0' + s.add_dependency 'cells', '~> 4.1.7' + s.add_dependency 'cells-rails', '~> 0.0.8' + s.add_dependency 'cells-haml', '~> 0.0.10' + s.add_dependency 'breadcrumbs_on_rails', '~> 3.0.1' + + # Style + s.add_dependency 'sass-rails', '~> 5.0' + + # JavaScript + s.add_dependency 'gon', '~> 6.2.0' + s.add_dependency 'react_on_rails', '9.0.3' + s.add_dependency 'mini_racer' + s.add_dependency 'webpacker' + + # Feature Flagging + s.add_dependency 'flipper', '~> 0.11' + s.add_dependency 'flipper-ui', '~> 0.11' + s.add_dependency 'flipper-active_record', '~> 0.11' +end diff --git a/db/migrate/10_create_fields.rb b/db/migrate/10_create_fields.rb index 937153b75..01fa42d6d 100644 --- a/db/migrate/10_create_fields.rb +++ b/db/migrate/10_create_fields.rb @@ -1,12 +1,12 @@ class CreateFields < ActiveRecord::Migration[5.1] def change - create_table :fields, id: :uuid do |t| + create_table :cortex_fields, id: :uuid do |t| t.string :name, null: false, index: true t.string :name_id, null: false, index: true t.string :field_type, null: false, index: true t.jsonb :metadata, null: false, default: {} t.jsonb :validations, null: false, default: {} - t.references :content_type, type: :uuid, null: false, foreign_key: true + t.references :content_type, type: :uuid, null: false, foreign_key: { to_table: :cortex_content_types } t.datetime :deleted_at, index: true t.timestamps null: false diff --git a/db/migrate/11_create_content_items.rb b/db/migrate/11_create_content_items.rb index 7c5b6d76d..6d3e918eb 100644 --- a/db/migrate/11_create_content_items.rb +++ b/db/migrate/11_create_content_items.rb @@ -1,11 +1,11 @@ class CreateContentItems < ActiveRecord::Migration[5.1] def change - create_table :content_items, id: :uuid do |t| + create_table :cortex_content_items, id: :uuid do |t| t.string :state, index: true - t.references :content_type, type: :uuid, null: false, foreign_key: true - t.references :tenant, type: :uuid, null: false, foreign_key: true - t.references :creator, type: :uuid, null: false, foreign_key: { to_table: :users } - t.references :updated_by, type: :uuid, foreign_key: { to_table: :users } + t.references :content_type, type: :uuid, null: false, foreign_key: { to_table: :cortex_content_types } + t.references :tenant, type: :uuid, null: false, foreign_key: { to_table: :cortex_tenants } + t.references :creator, type: :uuid, null: false, foreign_key: { to_table: :cortex_users } + t.references :updated_by, type: :uuid, foreign_key: { to_table: :cortex_users } t.datetime :deleted_at, index: true t.timestamps null: false diff --git a/db/migrate/12_create_field_items.rb b/db/migrate/12_create_field_items.rb index c2d0f99af..1b29bc39a 100644 --- a/db/migrate/12_create_field_items.rb +++ b/db/migrate/12_create_field_items.rb @@ -1,9 +1,9 @@ class CreateFieldItems < ActiveRecord::Migration[5.1] def change - create_table :field_items, id: :uuid do |t| + create_table :cortex_field_items, id: :uuid do |t| t.jsonb :data, null: false, default: {}, index: { using: :gin } - t.references :field, type: :uuid, null: false, foreign_key: true - t.references :content_item, type: :uuid, null: false, foreign_key: true + t.references :field, type: :uuid, null: false, foreign_key: { to_table: :cortex_fields } + t.references :content_item, type: :uuid, null: false, foreign_key: { to_table: :cortex_content_items } t.datetime :deleted_at, index: true t.timestamps null: false diff --git a/db/migrate/13_create_field_types.rb b/db/migrate/13_create_field_types.rb index ca6110401..63f0b90b4 100644 --- a/db/migrate/13_create_field_types.rb +++ b/db/migrate/13_create_field_types.rb @@ -1,5 +1,5 @@ class CreateFieldTypes < ActiveRecord::Migration[5.1] def change - create_table :field_types, id: :uuid + create_table :cortex_field_types, id: :uuid end end diff --git a/db/migrate/1_create_users.rb b/db/migrate/1_create_users.rb index dd9574578..0bc8d185f 100644 --- a/db/migrate/1_create_users.rb +++ b/db/migrate/1_create_users.rb @@ -1,6 +1,6 @@ class CreateUsers < ActiveRecord::Migration[5.1] def change - create_table :users, id: :uuid do |t| + create_table :cortex_users, id: :uuid do |t| t.string :email, null: false t.string :firstname, null: false t.string :lastname, null: false diff --git a/db/migrate/2_create_tenants.rb b/db/migrate/2_create_tenants.rb index c1aa1fffe..e8cdc43cd 100644 --- a/db/migrate/2_create_tenants.rb +++ b/db/migrate/2_create_tenants.rb @@ -1,6 +1,6 @@ class CreateTenants < ActiveRecord::Migration[5.1] def change - create_table :tenants, id: :uuid do |t| + create_table :cortex_tenants, id: :uuid do |t| t.string :name, limit: 50, null: false, index: { unique: true } t.string :name_id, null: false, index: { unique: true } t.text :description @@ -11,16 +11,16 @@ def change t.datetime :deleted_at t.datetime :active_at t.datetime :deactive_at - t.references :owner, type: :uuid, foreign_key: { to_table: :users } + t.references :owner, type: :uuid, foreign_key: { to_table: :cortex_users } t.datetime :deleted_at, index: true t.timestamps end - create_join_table :tenants, :users, column_options: { type: :uuid, index: true } - add_foreign_key :tenants_users, :tenants, type: :uuid - add_foreign_key :tenants_users, :users, type: :uuid + create_join_table :tenants, :users, table_name: :cortex_tenants_users, column_options: { type: :uuid, index: true } + add_foreign_key :cortex_tenants_users, :cortex_tenants, column: :tenant_id, type: :uuid + add_foreign_key :cortex_tenants_users, :cortex_users, column: :user_id, type: :uuid - add_reference :users, :active_tenant, type: :uuid, foreign_key: { to_table: :tenants } + add_reference :cortex_users, :active_tenant, type: :uuid, foreign_key: { to_table: :cortex_tenants } end end diff --git a/db/migrate/3_add_devise_to_users.rb b/db/migrate/3_add_devise_to_users.rb index 07dedf06e..18661c43b 100644 --- a/db/migrate/3_add_devise_to_users.rb +++ b/db/migrate/3_add_devise_to_users.rb @@ -1,6 +1,6 @@ class AddDeviseToUsers < ActiveRecord::Migration[5.1] def self.up - change_table(:users) do |t| + change_table(:cortex_users) do |t| ## Database authenticatable t.change :email, :string, :default => "" t.string :encrypted_password, :null => false, :default => "" @@ -35,8 +35,8 @@ def self.up # t.timestamps end - add_index :users, :email, :unique => true - add_index :users, :reset_password_token, :unique => true + add_index :cortex_users, :email, :unique => true + add_index :cortex_users, :reset_password_token, :unique => true # add_index :users, :confirmation_token, :unique => true # add_index :users, :unlock_token, :unique => true end diff --git a/db/migrate/4_create_roles.rb b/db/migrate/4_create_roles.rb index 004ed420c..bc44f54f9 100644 --- a/db/migrate/4_create_roles.rb +++ b/db/migrate/4_create_roles.rb @@ -1,6 +1,6 @@ class CreateRoles < ActiveRecord::Migration[5.1] def change - create_table :roles, id: :uuid do |t| + create_table :cortex_roles, id: :uuid do |t| t.string :name, index: true t.references :resource, type: :uuid, polymorphic: true, index: true @@ -8,8 +8,8 @@ def change t.timestamps null: false end - create_join_table :roles, :users, column_options: { type: :uuid, index: true } - add_foreign_key :roles_users, :roles, type: :uuid - add_foreign_key :roles_users, :users, type: :uuid + create_join_table :roles, :users, table_name: :cortex_roles_users, column_options: { type: :uuid, index: true } + add_foreign_key :cortex_roles_users, :cortex_roles, column: :role_id, type: :uuid + add_foreign_key :cortex_roles_users, :cortex_users, column: :user_id, type: :uuid end end diff --git a/db/migrate/5_create_permissions.rb b/db/migrate/5_create_permissions.rb index 43dba0698..82f97f896 100644 --- a/db/migrate/5_create_permissions.rb +++ b/db/migrate/5_create_permissions.rb @@ -1,6 +1,6 @@ class CreatePermissions < ActiveRecord::Migration[5.1] def change - create_table :permissions, id: :uuid do |t| + create_table :cortex_permissions, id: :uuid do |t| t.string :name, index: true t.string :resource_type, index: true t.uuid :resource_id, index: true @@ -9,8 +9,8 @@ def change t.timestamps null: false end - create_join_table :permissions, :roles, column_options: { type: :uuid, index: true } - add_foreign_key :permissions_roles, :permissions, type: :uuid - add_foreign_key :permissions_roles, :roles, type: :uuid + create_join_table :permissions, :roles, table_name: :cortex_permissions_roles, column_options: { type: :uuid, index: true } + add_foreign_key :cortex_permissions_roles, :cortex_permissions, column: :permission_id, type: :uuid + add_foreign_key :cortex_permissions_roles, :cortex_roles, column: :role_id, type: :uuid end end diff --git a/db/migrate/6_create_flipper_tables.rb b/db/migrate/6_create_flipper_tables.rb index ed9e77a04..b81a12aab 100644 --- a/db/migrate/6_create_flipper_tables.rb +++ b/db/migrate/6_create_flipper_tables.rb @@ -1,11 +1,11 @@ class CreateFlipperTables < ActiveRecord::Migration[5.1] def self.up - create_table :flipper_features, id: :uuid do |t| + create_table :cortex_flipper_features, id: :uuid do |t| t.string :key, null: false, index: { unique: true } t.timestamps null: false end - create_table :flipper_gates, id: :uuid do |t| + create_table :cortex_flipper_gates, id: :uuid do |t| t.string :feature_key, null: false, index: { unique: true } t.string :key, null: false, index: { unique: true } t.string :value, index: { unique: true } @@ -14,7 +14,7 @@ def self.up end def self.down - drop_table :flipper_gates - drop_table :flipper_features + drop_table :cortex_flipper_gates + drop_table :cortex_flipper_features end end diff --git a/db/migrate/7_create_contracts.rb b/db/migrate/7_create_contracts.rb index 693041198..bfd2fcd9a 100644 --- a/db/migrate/7_create_contracts.rb +++ b/db/migrate/7_create_contracts.rb @@ -1,8 +1,8 @@ class CreateContracts < ActiveRecord::Migration[5.1] def change - create_table :contracts, id: :uuid do |t| + create_table :cortex_contracts, id: :uuid do |t| t.string :name, null: false, index: true - t.references :tenant, type: :uuid, null: false, foreign_key: true + t.references :tenant, type: :uuid, null: false, foreign_key: { to_table: :cortex_tenants } t.datetime :deleted_at, index: true t.timestamps null: false diff --git a/db/migrate/8_create_decorators.rb b/db/migrate/8_create_decorators.rb index afe16916f..4adc52043 100644 --- a/db/migrate/8_create_decorators.rb +++ b/db/migrate/8_create_decorators.rb @@ -1,18 +1,18 @@ class CreateDecorators < ActiveRecord::Migration[5.1] def change - create_table :decorators, id: :uuid do |t| + create_table :cortex_decorators, id: :uuid do |t| t.string :name, null: false, index: true t.jsonb :data, null: false, default: {} - t.references :tenant, type: :uuid, null: false, foreign_key: true + t.references :tenant, type: :uuid, null: false, foreign_key: { to_table: :cortex_tenants } t.datetime :deleted_at, index: true t.timestamps null: false end - create_table :contentable_decorators, id: :uuid do |t| - t.references :decorator, type: :uuid, foreign_key: true - t.references :contentable, type: :uuid, polymorphic: true, index: { name: 'index_contentable_decorators_on_contentable' } - t.references :tenant, type: :uuid, null: false, foreign_key: true + create_table :cortex_contentable_decorators, id: :uuid do |t| + t.references :decorator, type: :uuid, foreign_key: { to_table: :cortex_decorators } + t.references :contentable, type: :uuid, polymorphic: true, index: { name: 'index_cortex_contentable_decorators_on_contentable' } + t.references :tenant, type: :uuid, null: false, foreign_key: { to_table: :cortex_tenants } t.datetime :deleted_at, index: true t.timestamps null: false diff --git a/db/migrate/9_create_content_types.rb b/db/migrate/9_create_content_types.rb index f243807a3..d88212ff4 100644 --- a/db/migrate/9_create_content_types.rb +++ b/db/migrate/9_create_content_types.rb @@ -1,12 +1,12 @@ class CreateContentTypes < ActiveRecord::Migration[5.1] def change - create_table :content_types, id: :uuid do |t| + create_table :cortex_content_types, id: :uuid do |t| t.string :name, null: false, index: true t.string :name_id, null: false, index: true t.text :description - t.references :creator, type: :uuid, null: false, foreign_key: { to_table: :users } - t.references :tenant, type: :uuid, null: false, foreign_key: true - t.references :contract, type: :uuid, null: false, foreign_key: true + t.references :creator, type: :uuid, null: false, foreign_key: { to_table: :cortex_users } + t.references :tenant, type: :uuid, null: false, foreign_key: { to_table: :cortex_tenants } + t.references :contract, type: :uuid, null: false, foreign_key: { to_table: :cortex_contracts } t.boolean :publishable, null: false, default: false t.string :icon, default: :help diff --git a/db/schema.rb b/db/schema.rb deleted file mode 100644 index 3504bee31..000000000 --- a/db/schema.rb +++ /dev/null @@ -1,258 +0,0 @@ -# This file is auto-generated from the current state of the database. Instead -# of editing this file, please use the migrations feature of Active Record to -# incrementally modify your database, and then regenerate this schema definition. -# -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). -# -# It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema.define(version: 13) do - - # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" - enable_extension "pgcrypto" - - create_table "content_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "state" - t.uuid "content_type_id", null: false - t.uuid "tenant_id", null: false - t.uuid "creator_id", null: false - t.uuid "updated_by_id" - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["content_type_id"], name: "index_content_items_on_content_type_id" - t.index ["creator_id"], name: "index_content_items_on_creator_id" - t.index ["deleted_at"], name: "index_content_items_on_deleted_at" - t.index ["state"], name: "index_content_items_on_state" - t.index ["tenant_id"], name: "index_content_items_on_tenant_id" - t.index ["updated_by_id"], name: "index_content_items_on_updated_by_id" - end - - create_table "content_types", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", null: false - t.string "name_id", null: false - t.text "description" - t.uuid "creator_id", null: false - t.uuid "tenant_id", null: false - t.uuid "contract_id", null: false - t.boolean "publishable", default: false, null: false - t.string "icon", default: "help" - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["contract_id"], name: "index_content_types_on_contract_id" - t.index ["creator_id"], name: "index_content_types_on_creator_id" - t.index ["deleted_at"], name: "index_content_types_on_deleted_at" - t.index ["name"], name: "index_content_types_on_name" - t.index ["name_id"], name: "index_content_types_on_name_id" - t.index ["tenant_id"], name: "index_content_types_on_tenant_id" - end - - create_table "contentable_decorators", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "decorator_id" - t.string "contentable_type" - t.uuid "contentable_id" - t.uuid "tenant_id", null: false - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["contentable_type", "contentable_id"], name: "index_contentable_decorators_on_contentable" - t.index ["decorator_id"], name: "index_contentable_decorators_on_decorator_id" - t.index ["deleted_at"], name: "index_contentable_decorators_on_deleted_at" - t.index ["tenant_id"], name: "index_contentable_decorators_on_tenant_id" - end - - create_table "contracts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", null: false - t.uuid "tenant_id", null: false - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["deleted_at"], name: "index_contracts_on_deleted_at" - t.index ["name"], name: "index_contracts_on_name" - t.index ["tenant_id"], name: "index_contracts_on_tenant_id" - end - - create_table "decorators", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", null: false - t.jsonb "data", default: {}, null: false - t.uuid "tenant_id", null: false - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["deleted_at"], name: "index_decorators_on_deleted_at" - t.index ["name"], name: "index_decorators_on_name" - t.index ["tenant_id"], name: "index_decorators_on_tenant_id" - end - - create_table "field_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.jsonb "data", default: {}, null: false - t.uuid "field_id", null: false - t.uuid "content_item_id", null: false - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["content_item_id"], name: "index_field_items_on_content_item_id" - t.index ["data"], name: "index_field_items_on_data", using: :gin - t.index ["deleted_at"], name: "index_field_items_on_deleted_at" - t.index ["field_id"], name: "index_field_items_on_field_id" - end - - create_table "field_types", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - end - - create_table "fields", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", null: false - t.string "name_id", null: false - t.string "field_type", null: false - t.jsonb "metadata", default: {}, null: false - t.jsonb "validations", default: {}, null: false - t.uuid "content_type_id", null: false - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["content_type_id"], name: "index_fields_on_content_type_id" - t.index ["deleted_at"], name: "index_fields_on_deleted_at" - t.index ["field_type"], name: "index_fields_on_field_type" - t.index ["name"], name: "index_fields_on_name" - t.index ["name_id"], name: "index_fields_on_name_id" - end - - create_table "flipper_features", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "key", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["key"], name: "index_flipper_features_on_key", unique: true - end - - create_table "flipper_gates", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "feature_key", null: false - t.string "key", null: false - t.string "value" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["feature_key"], name: "index_flipper_gates_on_feature_key", unique: true - t.index ["key"], name: "index_flipper_gates_on_key", unique: true - t.index ["value"], name: "index_flipper_gates_on_value", unique: true - end - - create_table "permissions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name" - t.string "resource_type" - t.uuid "resource_id" - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["deleted_at"], name: "index_permissions_on_deleted_at" - t.index ["name"], name: "index_permissions_on_name" - t.index ["resource_id"], name: "index_permissions_on_resource_id" - t.index ["resource_type"], name: "index_permissions_on_resource_type" - end - - create_table "permissions_roles", id: false, force: :cascade do |t| - t.uuid "permission_id", null: false - t.uuid "role_id", null: false - t.index ["permission_id"], name: "index_permissions_roles_on_permission_id" - t.index ["role_id"], name: "index_permissions_roles_on_role_id" - end - - create_table "roles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name" - t.string "resource_type" - t.uuid "resource_id" - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["deleted_at"], name: "index_roles_on_deleted_at" - t.index ["name"], name: "index_roles_on_name" - t.index ["resource_type", "resource_id"], name: "index_roles_on_resource_type_and_resource_id" - end - - create_table "roles_users", id: false, force: :cascade do |t| - t.uuid "role_id", null: false - t.uuid "user_id", null: false - t.index ["role_id"], name: "index_roles_users_on_role_id" - t.index ["user_id"], name: "index_roles_users_on_user_id" - end - - create_table "tenants", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", limit: 50, null: false - t.string "name_id", null: false - t.text "description" - t.uuid "parent_id" - t.integer "lft" - t.integer "rgt" - t.integer "depth" - t.datetime "deleted_at" - t.datetime "active_at" - t.datetime "deactive_at" - t.uuid "owner_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["deleted_at"], name: "index_tenants_on_deleted_at" - t.index ["name"], name: "index_tenants_on_name", unique: true - t.index ["name_id"], name: "index_tenants_on_name_id", unique: true - t.index ["owner_id"], name: "index_tenants_on_owner_id" - t.index ["parent_id"], name: "index_tenants_on_parent_id" - end - - create_table "tenants_users", id: false, force: :cascade do |t| - t.uuid "tenant_id", null: false - t.uuid "user_id", null: false - t.index ["tenant_id"], name: "index_tenants_users_on_tenant_id" - t.index ["user_id"], name: "index_tenants_users_on_user_id" - end - - create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "email", default: "", null: false - t.string "firstname", null: false - t.string "lastname", null: false - t.string "locale", limit: 30, default: "en_US", null: false - t.string "timezone", limit: 30, default: "EST", null: false - t.datetime "deleted_at" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.uuid "active_tenant_id" - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" - t.datetime "reset_password_sent_at" - t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false - t.datetime "current_sign_in_at" - t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" - t.index ["active_tenant_id"], name: "index_users_on_active_tenant_id" - t.index ["deleted_at"], name: "index_users_on_deleted_at" - t.index ["email"], name: "index_users_on_email", unique: true - t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true - end - - add_foreign_key "content_items", "content_types" - add_foreign_key "content_items", "tenants" - add_foreign_key "content_items", "users", column: "creator_id" - add_foreign_key "content_items", "users", column: "updated_by_id" - add_foreign_key "content_types", "contracts" - add_foreign_key "content_types", "tenants" - add_foreign_key "content_types", "users", column: "creator_id" - add_foreign_key "contentable_decorators", "decorators" - add_foreign_key "contentable_decorators", "tenants" - add_foreign_key "contracts", "tenants" - add_foreign_key "decorators", "tenants" - add_foreign_key "field_items", "content_items" - add_foreign_key "field_items", "fields" - add_foreign_key "fields", "content_types" - add_foreign_key "permissions_roles", "permissions" - add_foreign_key "permissions_roles", "roles" - add_foreign_key "roles_users", "roles" - add_foreign_key "roles_users", "users" - add_foreign_key "tenants", "users", column: "owner_id" - add_foreign_key "tenants_users", "tenants" - add_foreign_key "tenants_users", "users" - add_foreign_key "users", "tenants", column: "active_tenant_id" -end diff --git a/db/seeds.rb b/db/seeds.rb index 3f6bc2f5b..46067f344 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,32 +1,32 @@ # Create root tenant and user -cortex_tenant_seed = SeedData.cortex_tenant -example_tenant_seed = SeedData.example_tenant -example_subtenant_seed = SeedData.example_subtenant -example_contract_seed = SeedData.example_contract -user_seed = cortex_tenant_seed.creator +cortex_tenant_seed = Cortex.seed_data[:cortex_tenant] +example_tenant_seed = Cortex.seed_data[:example_tenant] +example_subtenant_seed = Cortex.seed_data[:example_subtenant] +example_contract_seed = Cortex.seed_data[:example_contract] +user_seed = cortex_tenant_seed[:creator] -existing_tenant = Tenant.find_by_name(cortex_tenant_seed.name) +existing_tenant = Cortex::Tenant.find_by_name(cortex_tenant_seed[:name]) unless existing_tenant - user = User.new(email: user_seed.email, - firstname: user_seed.firstname, - lastname: user_seed.lastname, - password: user_seed.password, - password_confirmation: user_seed.password) - cortex_tenant = Tenant.new(name: cortex_tenant_seed.name, - name_id: cortex_tenant_seed.name_id, - description: cortex_tenant_seed.description, + user = Cortex::User.new(email: user_seed[:email], + firstname: user_seed[:firstname], + lastname: user_seed[:lastname], + password: user_seed[:password], + password_confirmation: user_seed[:password]) + cortex_tenant = Cortex::Tenant.new(name: cortex_tenant_seed[:name], + name_id: cortex_tenant_seed[:name_id], + description: cortex_tenant_seed[:description], owner: user) - example_tenant = Tenant.new(name: example_tenant_seed.name, - name_id: example_tenant_seed.name_id, - description: example_tenant_seed.description, + example_tenant = Cortex::Tenant.new(name: example_tenant_seed[:name], + name_id: example_tenant_seed[:name_id], + description: example_tenant_seed[:description], owner: user) - example_subtenant = Tenant.new(name: example_subtenant_seed.name, - name_id: example_subtenant_seed.name_id, - description: example_subtenant_seed.description, + example_subtenant = Cortex::Tenant.new(name: example_subtenant_seed[:name], + name_id: example_subtenant_seed[:name_id], + description: example_subtenant_seed[:description], owner: user, parent: example_tenant) - example_contract = Contract.new(name: example_contract_seed.name, + example_contract = Cortex::Contract.new(name: example_contract_seed[:name], tenant: cortex_tenant) cortex_tenant.save! diff --git a/development.env b/development.env deleted file mode 100644 index ad64c2374..000000000 --- a/development.env +++ /dev/null @@ -1,2 +0,0 @@ -RAILS_ENV=development -PORT=3000 diff --git a/lib/cortex.rb b/lib/cortex.rb new file mode 100644 index 000000000..ee896b563 --- /dev/null +++ b/lib/cortex.rb @@ -0,0 +1,4 @@ +require 'cortex/engine' + +module Cortex +end diff --git a/lib/cortex/engine.rb b/lib/cortex/engine.rb new file mode 100644 index 000000000..51384f7ba --- /dev/null +++ b/lib/cortex/engine.rb @@ -0,0 +1,39 @@ +require 'devise' +require 'elasticsearch/rails/instrumentation' +require 'flipper' +require 'flipper-active_record' +require 'transitions' +require 'active_model/transitions' +require 'paranoia' +require 'rolify' +require 'breadcrumbs_on_rails' +require 'awesome_nested_set' +require 'react_on_rails' +require 'cells-rails' +require 'cells-haml' +require 'graphiql/rails' +require 'pomona' +require 'rack/cors' + +module Cortex + class Engine < ::Rails::Engine + isolate_namespace Cortex + + initializer "cortex.precompile_manifest" do |app| + app.config.assets.precompile += %w(cortex_manifest) + end + + initializer "cortex.add_middleware" do |app| + # Insert Rack::CORS as the first middleware + app.config.middleware.insert_before 0, Rack::Cors do + allow do + origins *((Cortex.config[:cors][:allowed_origins] || '*').split(',') + + [Cortex.config[:cors][:allowed_origins_regex] || '']) + resource '*', + :headers => :any, + :methods => [:get, :post, :options] + end + end + end + end +end diff --git a/lib/cortex/version.rb b/lib/cortex/version.rb new file mode 100644 index 000000000..f4f207862 --- /dev/null +++ b/lib/cortex/version.rb @@ -0,0 +1,3 @@ +module Cortex + VERSION = '0.1.0' +end diff --git a/lib/tasks/cortex.rake b/lib/tasks/cortex_tasks.rake similarity index 80% rename from lib/tasks/cortex.rake rename to lib/tasks/cortex_tasks.rake index 5b0d8d633..fbc4c32d3 100644 --- a/lib/tasks/cortex.rake +++ b/lib/tasks/cortex_tasks.rake @@ -5,7 +5,7 @@ Bundler.require(:default, Rails.env) namespace :cortex do desc 'Force re-creation of all elasticsearch mappings' task :rebuild_indexes => :environment do - [ContentType, ContentItem].each do |klass| + [Cortex::ContentType, Cortex::ContentItem].each do |klass| klass.__elasticsearch__.create_index! force: true klass.import end diff --git a/lib/tasks/elasticsearch.rake b/lib/tasks/elasticsearch_tasks.rake similarity index 100% rename from lib/tasks/elasticsearch.rake rename to lib/tasks/elasticsearch_tasks.rake diff --git a/lib/tasks/employer/blog.rake b/lib/tasks/employer/blog.rake deleted file mode 100644 index 9192982dd..000000000 --- a/lib/tasks/employer/blog.rake +++ /dev/null @@ -1,395 +0,0 @@ -Bundler.require(:default, Rails.env) - -namespace :employer do - namespace :blog do - desc 'Seed Employer Blog ContentType and Fields' - task seed: :environment do - example_tenant = Tenant.find_by_name('Example') - - def research_tree - tree = Tree.new - tree.add_node({name: "CB Research"}) - tree.add_node({name: "Third Party Research"}) - - tree - end - - def category_tree - tree = Tree.new - tree.add_node({ name: "Product News" }) - tree.add_node({ name: "Company News, Research and Trends" }) - tree.add_node({ name: "Client Success Stories" }) - tree.add_node({ name: "Recruiting Solutions" }) - tree.add_node({ name: "Employment Screening" }) - tree.add_node({ name: "Human Capital Management" }) - - tree - end - - def persona_tree - tree = Tree.new - tree.add_node({name: "General Audience"}) - tree.add_node({name: "Recruiters"}) - tree.add_node({name: "Sourcers"}) - tree.add_node({name: "Managers/Directors"}) - tree.add_node({name: "C-Level"}) - - tree - end - - puts "Creating Employer Blog ContentType..." - blog = ContentType.new({ - name: "Employer Blog", - name_id: "employer_blog", - description: "Blog for Employer", - icon: "description", - tenant: example_tenant, - creator: User.first, - contract: Contract.first, # TODO: This is obviously bad. This whole file is bad. - publishable: true - }) - blog.save! - - puts "Creating Fields..." - blog.fields.new(name: 'Body', name_id: 'body', field_type: 'text_field_type', metadata: {parse_widgets: true}, validations: { presence: true, length: { minimum: 50} }) - blog.fields.new(name: 'Title', name_id: 'title', field_type: 'text_field_type', validations: {presence: true, length: { maximum: 100 } }) - blog.fields.new(name: 'Description', name_id: 'description', field_type: 'text_field_type', validations: {presence: true, length: { maximum: 200, minimum: 50} }) - blog.fields.new(name: 'Slug', name_id: 'slug', field_type: 'text_field_type', validations: {presence: true, uniqueness: true, length: { maximum: 75 } }) - blog.fields.new(name: 'Author', name_id: 'author', field_type: 'author_field_type') - blog.fields.new(name: 'Tags', name_id: 'tags', field_type: 'tag_field_type') - blog.fields.new(name: 'Publish Date', name_id: 'publish_date', field_type: 'date_time_field_type', metadata: {state: 'Published'}) - blog.fields.new(name: 'Expiration Date', name_id: 'expiration_date', field_type: 'date_time_field_type', metadata: {state: 'Expired'}) - blog.fields.new(name: 'SEO Title', name_id: 'seo_title', field_type: 'text_field_type', validations: {presence: true, length: {maximum: 70}, uniqueness: true }) - blog.fields.new(name: 'SEO Description', name_id: 'seo_description', field_type: 'text_field_type', validations: {presence: true, length: {maximum: 160} }) - blog.fields.new(name: 'SEO Keywords', name_id: 'seo_keywords', field_type: 'tag_field_type') - blog.fields.new(name: 'No Index', name_id: 'no_index', field_type: 'boolean_field_type') - blog.fields.new(name: 'No Follow', name_id: 'no_follow', field_type: 'boolean_field_type') - blog.fields.new(name: 'No Snippet', name_id: 'no_snippet', field_type: 'boolean_field_type') - blog.fields.new(name: 'No ODP', name_id: 'no_odp', field_type: 'boolean_field_type') - blog.fields.new(name: 'No Archive', name_id: 'no_archive', field_type: 'boolean_field_type') - blog.fields.new(name: 'No Image Index', name_id: 'no_image_index', field_type: 'boolean_field_type') - blog.fields.new(name: 'Categories', name_id: 'categories', field_type: 'tree_field_type', metadata: {allowed_values: category_tree}, validations: {maximum: 2, minimum: 1}) - blog.fields.new(name: 'Research', name_id: 'research', field_type: 'tree_field_type', metadata: {allowed_values: research_tree}, validations: {minimum: 1}) - blog.fields.new(name: 'Persona', name_id: 'persona', field_type: 'tree_field_type', metadata: {allowed_values: persona_tree}) - blog.fields.new(name: 'Featured Image', name_id: 'featured_image', field_type: 'content_item_field_type', - metadata: { - field_name: 'Asset' - }) - - puts "Saving Employer Blog..." - blog.save! - - puts "Creating Wizard Decorators..." - wizard_hash = { - "steps": [ - { - "name": "Write", - "heading": "First thing's first..", - "description": "Author your post using Cortex's WYSIWYG editor.", - "columns": [ - { - "heading": "Writing Panel Sections's Optional Heading", - "grid_width": 12, - "display": { - "classes": [ - "text--right" - ] - }, - "elements": [ - { - "id": blog.fields.find_by_name('Title').id - }, - { - "id": blog.fields.find_by_name('Body').id, - "render_method": "wysiwyg", - "input": { - "display": { - "styles": { - "height": "500px" - } - } - } - } - ] - } - ] - }, - - { - "name": "Details", - "heading": "Let's talk about your post..", - "description": "Provide details and metadata that will enhance search or inform end-users.", - "columns": [ - { - "heading": "Publishing (Optional Heading)", - "grid_width": 6, - "elements": [ - { - "id": blog.fields.find_by_name('Description').id, - "tooltip": 'This is a short description and will be used as the preview text for a reader before they click into your full post.' - }, - { - "id": blog.fields.find_by_name('Publish Date').id - }, - { - "id": blog.fields.find_by_name('Expiration Date').id - } - ] - }, - { - "grid_width": 6, - "elements": [ - { - "id": blog.fields.find_by_name('Tags').id - }, - { - "id": blog.fields.find_by_name('Slug').id, - "tooltip": "This is your post's URL. Between each word, place a hyphen. Best if between 35-50 characters and don't include years/dates." - }, - { - "id": blog.fields.find_by_name('Author').id - } - ] - } - ] - }, - { - "name": "Categorize", - "heading": "Sort Into Categories..", - "description": "Select the categories that best describe your post.", - "columns": [ - { - "heading": "Publishing (Optional Heading)", - "grid_width": 4, - "elements": [ - { - "id": blog.fields.find_by_name('Categories').id, - "render_method": "checkboxes", - "tooltip": "Please select 1-2 Categories." - } - ] - }, - { - "grid_width": 4, - "elements": [ - { - "id": blog.fields.find_by_name('Persona').id, - "render_method": "dropdown", - "tooltip": "If this is for a specific role or audience, please select. If it's for everyone, choose general audience." - } - ] - }, - { - "grid_width": 4, - "elements": [ - { - "id": blog.fields.find_by_name('Research').id, - "render_method": "dropdown", - "tooltip": "If your post is based on research, please select if it's CB owned or created (including Emsi), or if we're reporting on someone else's data." - } - ] - } - ] - }, - { - "name": "Add Featured Image", - "description": "Add an image to feature with this Blog Post.", - "columns": [ - { - "heading": "Image (Optional Heading)", - "grid_width": 12, - "elements": [ - { - "id": blog.fields.find_by_name('Featured Image').id, - "render_method": "popup" - } - ] - } - ] - }, - { - "name": "Search", - "heading": "How can others find your post..", - "description": "Provide SEO metadata to help your post get found by your Users!", - "columns": [ - { - "grid_width": 6, - "elements": [ - { - "id": blog.fields.find_by_name('SEO Title').id, - "tooltip": 'Please use <70 characters for your SEO title for optimal appearance in search results.' - }, - { - "id": blog.fields.find_by_name('SEO Description').id, - "tooltip": 'The description should optimally be between 150-160 characters and keyword rich.' - }, - { - "id": blog.fields.find_by_name('SEO Keywords').id, - "tooltip": 'Utilize the recommended keywords as tags to boost your SEO performance.' - } - ] - }, - { - "grid_width": 6, - "description": "Select these if you don't want your post to be indexed by search engines like Google", - "elements": [ - { - "id": blog.fields.find_by_name('No Index').id - }, - { - "id": blog.fields.find_by_name('No Follow').id - }, - { - "id": blog.fields.find_by_name('No Snippet').id - }, - { - "id": blog.fields.find_by_name('No ODP').id - }, - { - "id": blog.fields.find_by_name('No Archive').id - }, - { - "id": blog.fields.find_by_name('No Image Index').id - } - ] - } - ] - } - ] - } - - blog_wizard_decorator = Decorator.new(name: "Wizard", data: wizard_hash, tenant: example_tenant) - blog_wizard_decorator.save! - - ContentableDecorator.create!({ - decorator_id: blog_wizard_decorator.id, - contentable_id: blog.id, - contentable_type: 'ContentType', - tenant: example_tenant - }) - - puts "Creating Index Decorators..." - index_hash = { - "columns": - [ - { - "name": "Author", - "grid_width": 2, - "cells": [{ - "field": { - "method": "creator.email" - }, - "display": { - "classes": [ - "circular" - ] - } - }] - }, - { - "name": "Post Details", - "cells": [ - { - "field": { - "id": blog.fields.find_by_name('Title').id - }, - "display": { - "classes": [ - "bold", - "upcase" - ] - } - }, - { - "field": { - "id": blog.fields.find_by_name('Slug').id - } - }, - { - "field": { - "method": "publish_state" - } - } - ] - }, - { - "name": "Tags", - "cells": [ - { - "field": { - "id": blog.fields.find_by_name('Tags').id - }, - "display": { - "classes": [ - "tag", - "rounded" - ] - } - } - ] - } - ] - } - - blog_index_decorator = Decorator.new(name: "Index", data: index_hash, tenant: example_tenant) - blog_index_decorator.save! - - ContentableDecorator.create!({ - decorator_id: blog_index_decorator.id, - contentable_id: blog.id, - contentable_type: 'ContentType', - tenant: example_tenant - }) - - puts "Creating RSS Decorators..." - rss_hash = { - "channel": { - "title": { "string": "Employer Blog" }, - "link": { "string": "http://resources.careerbuilder.com" }, - "description": { "string": "News and trends, best practices, product news and more from CareerBuilder's experts." }, - "language": { "string": "en-US" } - }, - "item": { - "title": { "field": blog.fields.find_by_name('Title').id }, - "description": { "field": blog.fields.find_by_name('Description').id }, - "link": { "method": { - "name": "rss_url", - "args": ["https://resources.careerbuilder.com/", blog.fields.find_by_name('Slug').id] - } - }, - "pubDate": { "method": { - "name": "rss_date", - "args": [blog.fields.find_by_name('Publish Date').id] - } - }, - "author": { "method": { - "name": "rss_author", - "args": [blog.fields.find_by_name('Author').id] - } - }, - "category": { "transaction": { - "name": "get_field_tree_list", - "args": {field_id: blog.fields.find_by_name('Categories').id} - }, "multiple": "," - }, - "media:content": { "media": - { "field": blog.fields.find_by_name('Featured Image').id, - "medium": "image" - } - }, - "content": { "field": blog.fields.find_by_name('Body').id, "encode": true } - } - } - - blog_rss_decorator = Decorator.new(name: "Rss", data: rss_hash, tenant: example_tenant) - blog_rss_decorator.save! - - ContentableDecorator.create!({ - decorator_id: blog_rss_decorator.id, - contentable_id: blog.id, - contentable_type: 'ContentType', - tenant: example_tenant - }) - end - end -end diff --git a/lib/tasks/employer/integration.rake b/lib/tasks/employer/integration.rake deleted file mode 100644 index b2efaaa6c..000000000 --- a/lib/tasks/employer/integration.rake +++ /dev/null @@ -1,261 +0,0 @@ -Bundler.require(:default, Rails.env) - -namespace :employer do - namespace :integration do - desc 'Seed Employer Integration ContentType and Fields' - task seed: :environment do - def category_tree - tree = Tree.new - tree.add_node({ name: "Category 1" }) - tree.add_node({ name: "Category 2" }) - tree.add_node({ name: "Category 3" }) - tree - end - - puts 'Creating Employer Integration ContentType...' - integration = ContentType.new({ - name: "Employer Integration", - description: "3rd Party CareerBuilder Integrations", - icon: "device hub", - creator_id: 1, - contract_id: 1, - publishable: true - }) - integration.save! - - puts "Creating Fields..." - integration.fields.new(name: 'Body', field_type: 'text_field_type', metadata: {parse_widgets: true}, validations: { presence: true }) - integration.fields.new(name: 'Title', field_type: 'text_field_type', validations: {presence: true}) - integration.fields.new(name: 'Description', field_type: 'text_field_type', validations: {presence: true}) - integration.fields.new(name: 'Slug', field_type: 'text_field_type', validations: {presence: true, uniqueness: true}) - integration.fields.new(name: 'Tags', field_type: 'tag_field_type') - integration.fields.new(name: 'Publish Date', field_type: 'date_time_field_type', metadata: {state: 'Published'}) - integration.fields.new(name: 'Expiration Date', field_type: 'date_time_field_type', metadata: {state: 'Expired'}) - integration.fields.new(name: 'SEO Title', field_type: 'text_field_type', validations: {presence: true, uniqueness: true }) - integration.fields.new(name: 'SEO Description', field_type: 'text_field_type', validations: {presence: true}) - integration.fields.new(name: 'SEO Keywords', field_type: 'tag_field_type') - integration.fields.new(name: 'No Index', field_type: 'boolean_field_type') - integration.fields.new(name: 'No Follow', field_type: 'boolean_field_type') - integration.fields.new(name: 'No Snippet', field_type: 'boolean_field_type') - integration.fields.new(name: 'No ODP', field_type: 'boolean_field_type') - integration.fields.new(name: 'No Archive', field_type: 'boolean_field_type') - integration.fields.new(name: 'No Image Index', field_type: 'boolean_field_type') - integration.fields.new(name: 'Categories', field_type: 'tree_field_type', metadata: {allowed_values: category_tree}) - integration.fields.new(name: 'Featured Image', field_type: 'content_item_field_type', - metadata: { - field_name: 'Asset' - }) - - puts "Saving Employer Integration..." - integration.save! - - puts "Creating Wizard Decorators..." - wizard_hash = { - "steps": [ - { - "name": "Write", - "columns": [ - { - "grid_width": 12, - "elements": [ - { - "id": integration.fields.find_by_name('Title').id - }, - { - "id": integration.fields.find_by_name('Body').id, - "render_method": "wysiwyg", - "input": { - "display": { - "styles": { - "height": "500px" - } - } - } - } - ] - } - ] - }, - - { - "name": "Details", - "columns": [ - { - "grid_width": 6, - "elements": [ - { - "id": integration.fields.find_by_name('Description').id, - "tooltip": 'This is a short description and will be used as the preview text for an employer before they click into the integration.' - }, - { - "id": integration.fields.find_by_name('Publish Date').id - }, - { - "id": integration.fields.find_by_name('Expiration Date').id - } - ] - }, - { - "grid_width": 6, - "elements": [ - { - "id": integration.fields.find_by_name('Tags').id - }, - { - "id": integration.fields.find_by_name('Slug').id, - "tooltip": "This is your integrations's URL. Between each word, place a hyphen. Best if between 35-50 characters and don't include years/dates." - } - ] - } - ] - }, - { - "name": "Categorize", - "columns": [ - { - "grid_width": 4, - "elements": [ - { - "id": integration.fields.find_by_name('Categories').id, - "render_method": "checkboxes" - } - ] - }, - { - "grid_width": 8, - "elements": [ - { - "id": integration.fields.find_by_name('Featured Image').id, - "render_method": "popup" - } - ] - } - ] - }, - { - "name": "Search", - "columns": [ - { - "grid_width": 6, - "elements": [ - { - "id": integration.fields.find_by_name('SEO Title').id, - "tooltip": 'Please use <70 characters for your SEO title for optimal appearance in search results.' - }, - { - "id": integration.fields.find_by_name('SEO Description').id, - "tooltip": 'The description should optimally be between 150-160 characters and keyword rich.' - }, - { - "id": integration.fields.find_by_name('SEO Keywords').id, - "tooltip": 'Utilize the recommended keywords as tags to boost your SEO performance.' - } - ] - }, - { - "grid_width": 6, - "description": "Select these if you don't want your integration to be indexed by search engines like Google", - "elements": [ - { - "id": integration.fields.find_by_name('No Index').id - }, - { - "id": integration.fields.find_by_name('No Follow').id - }, - { - "id": integration.fields.find_by_name('No Snippet').id - }, - { - "id": integration.fields.find_by_name('No ODP').id - }, - { - "id": integration.fields.find_by_name('No Archive').id - }, - { - "id": integration.fields.find_by_name('No Image Index').id - } - ] - } - ] - } - ] - } - - wizard_decorator = Decorator.new(name: "Wizard", data: wizard_hash) - wizard_decorator.save! - - ContentableDecorator.create!({ - decorator_id: wizard_decorator.id, - contentable_id: integration.id, - contentable_type: 'ContentType' - }) - - puts "Creating Index Decorators..." - index_hash = { - "columns": - [ - { - "name": "Title", - "grid_width": 3, - "cells": [{ - "field": { - "id": integration.fields.find_by_name('Title').id - } - }] - }, - { - "name": "Integration Details", - "cells": [ - { - "field": { - "id": integration.fields.find_by_name('Description').id - }, - "display": { - "classes": [ - "bold", - "upcase" - ] - } - }, - { - "field": { - "id": integration.fields.find_by_name('Slug').id - } - }, - { - "field": { - "method": "publish_state" - } - } - ] - }, - { - "name": "Tags", - "cells": [ - { - "field": { - "id": integration.fields.find_by_name('Tags').id - }, - "display": { - "classes": [ - "tag", - "rounded" - ] - } - } - ] - } - ] - } - - index_decorator = Decorator.new(name: "Index", data: index_hash) - index_decorator.save! - - ContentableDecorator.create!({ - decorator_id: index_decorator.id, - contentable_id: integration.id, - contentable_type: 'ContentType' - }) - end - end -end diff --git a/log/.keep b/log/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/node_package/.babelrc b/node_package/.babelrc new file mode 100644 index 000000000..75e4c04e4 --- /dev/null +++ b/node_package/.babelrc @@ -0,0 +1,26 @@ +{ + "presets": [ + [ + "env", + { + "modules": false, + "targets": { + "browsers": "> 1%", + "uglify": true + }, + "useBuiltIns": true + } + ], + "react" + ], + "plugins": [ + "syntax-dynamic-import", + "transform-object-rest-spread", + [ + "transform-class-properties", + { + "spec": true + } + ] + ] +} diff --git a/app/javascript/bundles/cortex/components/side_bar/environment_flag.jsx b/node_package/src/components/side_bar/environment_flag.jsx similarity index 89% rename from app/javascript/bundles/cortex/components/side_bar/environment_flag.jsx rename to node_package/src/components/side_bar/environment_flag.jsx index 2d21d4c04..11ce32808 100644 --- a/app/javascript/bundles/cortex/components/side_bar/environment_flag.jsx +++ b/node_package/src/components/side_bar/environment_flag.jsx @@ -2,7 +2,7 @@ import React from 'react'; class EnvironmentFlag extends React.PureComponent { render() { - const { environment, environment_abbreviated } = this.props + const {environment, environment_abbreviated} = this.props if (environment === 'production') return null const flagClass = `mdl-navigation__link nav__item environment environment--${environment}` return ( diff --git a/app/javascript/bundles/cortex/components/side_bar/tenant_list.jsx b/node_package/src/components/side_bar/tenant_list.jsx similarity index 85% rename from app/javascript/bundles/cortex/components/side_bar/tenant_list.jsx rename to node_package/src/components/side_bar/tenant_list.jsx index d5be71054..1ed74f8d9 100644 --- a/app/javascript/bundles/cortex/components/side_bar/tenant_list.jsx +++ b/node_package/src/components/side_bar/tenant_list.jsx @@ -12,7 +12,7 @@ import Avatar from 'material-ui/Avatar'; import IconButton from 'material-ui/IconButton'; import TenantAncestryList from '../../helpers/tenant_ancestry'; -const TenantItem = ({ id, name, description = 'Tenant Description', children }, tenantClicked, tenantActive, subTenantListClick) => ( +const TenantItem = ({id, name, description = 'Tenant Description', children}, tenantClicked, tenantActive, subTenantListClick) => (
@@ -28,13 +28,13 @@ const TenantItem = ({ id, name, description = 'Tenant Description', children }, class TenantList extends React.PureComponent { renderTenants = () => { - const { activeTenant, parentTenant, selectTenant, subTenantListClicked, tenantLookupTable } = this.props; + const {activeTenant, parentTenant, selectTenant, subTenantListClicked, tenantLookupTable} = this.props; const listedTenantIds = parentTenant === null ? tenantLookupTable.topLevel : tenantLookupTable[parentTenant].children; return listedTenantIds.map((tenant_id, index) => TenantItem(tenantLookupTable[tenant_id], selectTenant(tenantLookupTable[tenant_id]), activeTenant.id === tenantLookupTable[tenant_id].id, subTenantListClicked(tenantLookupTable[tenant_id].id))) }; - tenantParentHeader = ({ parentTenant, tenantLookupTable, paginateBack }) => { + tenantParentHeader = ({parentTenant, tenantLookupTable, paginateBack}) => { return (
@@ -46,7 +46,7 @@ class TenantList extends React.PureComponent { }; render() { - const { active, tenantSyncedWithDB } = this.props + const {active, tenantSyncedWithDB} = this.props const tenantListClass = active ? 'tenant__list_wrapper' : 'tenant__list_wrapper hidden'; return (
diff --git a/app/javascript/bundles/cortex/components/wizard/column.jsx b/node_package/src/components/wizard/column.jsx similarity index 52% rename from app/javascript/bundles/cortex/components/wizard/column.jsx rename to node_package/src/components/wizard/column.jsx index 071721b58..2db3e6fdb 100644 --- a/app/javascript/bundles/cortex/components/wizard/column.jsx +++ b/node_package/src/components/wizard/column.jsx @@ -7,20 +7,20 @@ class Column extends React.PureComponent { } renderElements = () => { - const { contentItemFieldLookup, elements } = this.props + const {contentItemFieldLookup, elements} = this.props return elements.filter(element => element.id).map((element, index) => { - let fieldItemId = element.id - return + let fieldItemId = element.id + return }) } render() { - const { heading, grid_width, display, description } = this.props + const {heading, grid_width, display, description} = this.props return (
- { description &&

{description}

} - { this.renderElements() } + {description &&

{description}

} + {this.renderElements()}
) } diff --git a/app/javascript/bundles/cortex/components/wizard/field.jsx b/node_package/src/components/wizard/field.jsx similarity index 100% rename from app/javascript/bundles/cortex/components/wizard/field.jsx rename to node_package/src/components/wizard/field.jsx diff --git a/node_package/src/components/wizard/step.jsx b/node_package/src/components/wizard/step.jsx new file mode 100644 index 000000000..88116a05d --- /dev/null +++ b/node_package/src/components/wizard/step.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import Column from './column' + +class Step extends React.PureComponent { + renderColumn = (column, index) => { + return + } + + render() { + const {name, heading, contentItemFieldLookup, description, columns} = this.props + + return ( +
+ {columns.map(this.renderColumn)} +
+ ) + } +} + +export default Step diff --git a/app/javascript/bundles/cortex/constants/tenant_switcher.jsx b/node_package/src/constants/tenant_switcher.jsx similarity index 100% rename from app/javascript/bundles/cortex/constants/tenant_switcher.jsx rename to node_package/src/constants/tenant_switcher.jsx diff --git a/app/javascript/bundles/cortex/constants/type_constants.jsx b/node_package/src/constants/type_constants.jsx similarity index 100% rename from app/javascript/bundles/cortex/constants/type_constants.jsx rename to node_package/src/constants/type_constants.jsx diff --git a/app/javascript/bundles/cortex/constants/wizard.jsx b/node_package/src/constants/wizard.jsx similarity index 100% rename from app/javascript/bundles/cortex/constants/wizard.jsx rename to node_package/src/constants/wizard.jsx diff --git a/app/javascript/bundles/cortex/containers/layout.jsx b/node_package/src/containers/layout.jsx similarity index 64% rename from app/javascript/bundles/cortex/containers/layout.jsx rename to node_package/src/containers/layout.jsx index 14426cbb8..82cbfa5ac 100644 --- a/app/javascript/bundles/cortex/containers/layout.jsx +++ b/node_package/src/containers/layout.jsx @@ -1,11 +1,11 @@ import React from 'react'; -import { connect } from 'react-redux'; +import {connect} from 'react-redux'; import TenantSwitcherContainer from './tenant_switcher_container'; import WizardContainer from './wizard_container'; function select(state) { - return { data: state }; + return {data: state}; } class Layout extends React.PureComponent { @@ -14,17 +14,17 @@ class Layout extends React.PureComponent { } render() { - const { data, dispatch, temporary_render } = this.props; + const {data, dispatch, temporary_render} = this.props; return ( {/* remove temporary_render once containers are all connected */} - { temporary_render === 'TenantSwitcher' && - - } - { temporary_render === 'Wizard' && - - } + {temporary_render === 'TenantSwitcher' && + + } + {temporary_render === 'Wizard' && + + } ) } diff --git a/app/javascript/bundles/cortex/containers/tenant_switcher_container.jsx b/node_package/src/containers/tenant_switcher_container.jsx similarity index 77% rename from app/javascript/bundles/cortex/containers/tenant_switcher_container.jsx rename to node_package/src/containers/tenant_switcher_container.jsx index 29793ff5d..7055fb793 100644 --- a/app/javascript/bundles/cortex/containers/tenant_switcher_container.jsx +++ b/node_package/src/containers/tenant_switcher_container.jsx @@ -29,19 +29,23 @@ class TenantSwitcherContainer extends React.PureComponent { this.railsAPI = SetRailsAPIService(this.props.railsContext, this.props.data) this.tenantLookupTable = TenantLookup(this.props.data.tenants) } + selectTenant = (tenant) => () => { this.updateTenant(tenant) this.props.dispatch({type: SELECT_TENANT, payload: tenant}) } updateTenant = (tenant) => { - const { currentUser, activeTenant, csrf_token } = this.props.data + const {currentUser, activeTenant, csrf_token} = this.props.data const self = this; this.railsAPI.post('/admin_update/tenant_change', { active_tenant: activeTenant.id, requested_tenant: tenant.id, authenticity_token: csrf_token }).then(response => { - self.props.dispatch({type: TENANT_UPDATED, payload: Object.assign({}, currentUser, {active_tenant: response.data})}) + self.props.dispatch({ + type: TENANT_UPDATED, + payload: Object.assign({}, currentUser, {active_tenant: response.data}) + }) self.layoutWrapper.classList.remove('sidebar--tenant-display') window.location = '/admin/dashboards'; // TODO: remove when we fully switch to React }).catch(error => { @@ -50,7 +54,7 @@ class TenantSwitcherContainer extends React.PureComponent { } paginateBack = () => { - const { parentTenant } = this.props.data; + const {parentTenant} = this.props.data; this.props.dispatch({type: PAGINATE_BACK, payload: this.tenantLookupTable[parentTenant].parent_id}) } subTenantListClicked = (parent_id) => () => { @@ -64,6 +68,7 @@ class TenantSwitcherContainer extends React.PureComponent { this.props.dispatch({type: TOGGLE_SIDEBAR}) this.layoutWrapper.className = this.props.data.sidebarExpanded ? 'sidebar--collapsed' : ''; } + render() { const { environment, @@ -78,26 +83,27 @@ class TenantSwitcherContainer extends React.PureComponent { return (