diff --git a/app.json b/app.json new file mode 100644 index 00000000..97bc07a7 --- /dev/null +++ b/app.json @@ -0,0 +1,65 @@ +{ + "name": "notebook", + "scripts": { + }, + "env": { + "AWS_ACCESS_KEY_ID": { + "required": true + }, + "AWS_REGION": { + "required": true + }, + "AWS_SECRET_ACCESS_KEY": { + "required": true + }, + "GOOGLE_TRANSLATE_API_KEY": { + "required": true + }, + "HEROKU_POSTGRESQL_BROWN_URL": { + "required": true + }, + "LANG": { + "required": true + }, + "LOG_LEVEL": { + "required": true + }, + "RACK_ENV": { + "required": true + }, + "RAILS_ENV": { + "required": true + }, + "RAILS_SERVE_STATIC_FILES": { + "required": true + }, + "RUBY_GC_HEAP_GROWTH_FACTOR": { + "required": true + }, + "S3_BUCKET_NAME": { + "required": true + }, + "SECRET_KEY_BASE": { + "required": true + }, + "SECRET_TOKEN": { + "required": true + }, + "STRIPE_API_KEY": { + "required": true + }, + "STRIPE_PUBLISHABLE_KEY": { + "required": true + } + }, + "formation": { + }, + "addons": [ + "heroku-postgresql" + ], + "buildpacks": [ + { + "url": "heroku/ruby" + } + ] +} diff --git a/app/assets/images/card-headers/floras.jpg b/app/assets/images/card-headers/floras.jpg new file mode 100644 index 00000000..834bcfe9 Binary files /dev/null and b/app/assets/images/card-headers/floras.jpg differ diff --git a/app/assets/javascripts/floras.coffee b/app/assets/javascripts/floras.coffee new file mode 100644 index 00000000..24f83d18 --- /dev/null +++ b/app/assets/javascripts/floras.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/floras.scss b/app/assets/stylesheets/floras.scss new file mode 100644 index 00000000..18b2d428 --- /dev/null +++ b/app/assets/stylesheets/floras.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Floras controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/content_controller.rb b/app/controllers/content_controller.rb index c4bbacef..e4c22707 100644 --- a/app/controllers/content_controller.rb +++ b/app/controllers/content_controller.rb @@ -33,13 +33,13 @@ class ContentController < ApplicationController 'content_type': content_type.name, 'content_owner': current_user.present? && current_user.id == @content.user_id, 'logged_in_user': current_user.present? - }) + }) if Rails.env.production? else Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'viewed recently-modified content', { 'content_type': content_type.name, 'content_owner': current_user.present? && current_user.id == @content.user_id, 'logged_in_user': current_user.present? - }) + }) if Rails.env.production? end end @@ -94,7 +94,7 @@ class ContentController < ApplicationController Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'created content', { 'content_type': content_type.name - }) + }) if Rails.env.production? if @content.save if params.key? 'image_uploads' @@ -117,7 +117,7 @@ class ContentController < ApplicationController Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'updated content', { 'content_type': content_type.name - }) + }) if Rails.env.production? if params.key? 'image_uploads' upload_files params['image_uploads'], content_type.name, @content.id @@ -156,7 +156,7 @@ class ContentController < ApplicationController 'content_type': content_type, 'image_size_kb': image_size_kb, 'first five images': current_user.image_uploads.count <= 5 - }) + }) if Rails.env.production? end end @@ -170,7 +170,7 @@ class ContentController < ApplicationController Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'deleted content', { 'content_type': content_type.name - }) + }) if Rails.env.production? @content.destroy diff --git a/app/controllers/export_controller.rb b/app/controllers/export_controller.rb index a641e486..7c44ead2 100644 --- a/app/controllers/export_controller.rb +++ b/app/controllers/export_controller.rb @@ -4,14 +4,14 @@ class ExportController < ApplicationController def index Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'viewed export page', { 'content count': current_user.content_count - }) + }) if Rails.env.production? end def report_to_mixpanel format, scope Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'exported content', { 'export format': format, 'scope': scope - }) + }) if Rails.env.production? end # Formats diff --git a/app/controllers/floras_controller.rb b/app/controllers/floras_controller.rb new file mode 100644 index 00000000..243c9a00 --- /dev/null +++ b/app/controllers/floras_controller.rb @@ -0,0 +1,25 @@ +class FlorasController < ContentController + private + + def content_params + params.require(:flora).permit(content_param_list) + end + + def content_param_list + %i( + name description aliases universe_id + order family genus + colorings size smell taste + fruits seeds nuts berries medicinal_purposes + reproduction seasonality + privacy + notes private_notes + ) + [ + custom_attribute_values: [:name, :value], + flora_relationships_attributes: [:id, :related_flora_id, :_destroy], + flora_magical_effects_attributes: [:id, :magic_id, :_destroy], + flora_locations_attributes: [:id, :location_id, :_destroy], + flora_eaten_by_attributes: [:id, :creature_id, :_destroy], + ] + end +end diff --git a/app/controllers/image_upload_controller.rb b/app/controllers/image_upload_controller.rb index 1e6e3c19..f8b6536e 100644 --- a/app/controllers/image_upload_controller.rb +++ b/app/controllers/image_upload_controller.rb @@ -28,7 +28,7 @@ class ImageUploadController < ApplicationController Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'deleted image', { 'image_size_kb': reclaimed_space_kb - }) + }) if Rails.env.production? render json: { success: result }, status: 200 end diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 91711e43..4a5e1131 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -7,7 +7,7 @@ class SubscriptionsController < ApplicationController Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'viewed billing page', { 'current billing plan': current_user.selected_billing_plan_id, 'content count': current_user.content_count - }) + }) if Rails.env.production? # We only support a single billing plan right now, so just grab the first one. If they don't have an active plan, # we also treat them as if they have a Starter plan. @@ -163,7 +163,7 @@ class SubscriptionsController < ApplicationController Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'viewed payment method page', { 'current billing plan': current_user.selected_billing_plan_id, 'content count': current_user.content_count - }) + }) if Rails.env.production? end def information_change @@ -300,7 +300,7 @@ class SubscriptionsController < ApplicationController end def stripe_webhook - Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'stripe webhook') + Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(current_user.id, 'stripe webhook') if Rails.env.production? #todo handle webhooks end diff --git a/app/controllers/universes_controller.rb b/app/controllers/universes_controller.rb index 31a1f5ea..840a0f6a 100644 --- a/app/controllers/universes_controller.rb +++ b/app/controllers/universes_controller.rb @@ -1,4 +1,19 @@ class UniversesController < ContentController + + # TODO: pull list of content types out from some centralized list somewhere + [ + :characters, :locations, :items, :creatures, :races, :religions, :groups, :magics, :languages, :floras, :scenes + ].each do |content_type_name| + define_method content_type_name do + @content_type = content_type_name.to_s.singularize.capitalize.constantize + + @universe = Universe.find(params[:id]) + @content_list = @universe.send(content_type_name).is_public.order(:name) + + render :content_list + end + end + private def content_params diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e1bcadf2..eb5e4827 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -8,6 +8,6 @@ class UsersController < ApplicationController Mixpanel::Tracker.new(Rails.application.config.mixpanel_token).track(@user.id, 'viewed profile', { 'sharing any content': @user.public_content_count != 0 - }) + }) if Rails.env.production? end end diff --git a/app/helpers/floras_helper.rb b/app/helpers/floras_helper.rb new file mode 100644 index 00000000..392b6f67 --- /dev/null +++ b/app/helpers/floras_helper.rb @@ -0,0 +1,2 @@ +module FlorasHelper +end diff --git a/app/models/concerns/has_content.rb b/app/models/concerns/has_content.rb index 3c4e1c4e..4f6af358 100644 --- a/app/models/concerns/has_content.rb +++ b/app/models/concerns/has_content.rb @@ -17,6 +17,7 @@ module HasContent has_many :magics has_many :languages has_many :groups + has_many :floras # Collective content types has_many :scenes diff --git a/app/models/content_groupers/flora_eaten_by.rb b/app/models/content_groupers/flora_eaten_by.rb new file mode 100644 index 00000000..793b4ed8 --- /dev/null +++ b/app/models/content_groupers/flora_eaten_by.rb @@ -0,0 +1,8 @@ +class FloraEatenBy < ActiveRecord::Base + include HasContentLinking + + belongs_to :user + + belongs_to :flora + belongs_to :creature +end diff --git a/app/models/content_groupers/flora_location.rb b/app/models/content_groupers/flora_location.rb new file mode 100644 index 00000000..38e581fb --- /dev/null +++ b/app/models/content_groupers/flora_location.rb @@ -0,0 +1,8 @@ +class FloraLocation < ActiveRecord::Base + include HasContentLinking + + belongs_to :user + + belongs_to :flora + belongs_to :location +end diff --git a/app/models/content_groupers/flora_magical_effect.rb b/app/models/content_groupers/flora_magical_effect.rb new file mode 100644 index 00000000..e9e8dbf1 --- /dev/null +++ b/app/models/content_groupers/flora_magical_effect.rb @@ -0,0 +1,8 @@ +class FloraMagicalEffect < ActiveRecord::Base + include HasContentLinking + + belongs_to :user + + belongs_to :flora + belongs_to :magic, class_name: 'Magic' +end diff --git a/app/models/content_groupers/flora_relationship.rb b/app/models/content_groupers/flora_relationship.rb new file mode 100644 index 00000000..e1ef33f7 --- /dev/null +++ b/app/models/content_groupers/flora_relationship.rb @@ -0,0 +1,21 @@ +class FloraRelationship < ActiveRecord::Base + include HasContentLinking + LINK_TYPE = :two_way + + belongs_to :user + + belongs_to :flora + belongs_to :related_flora, class_name: 'Flora' + + after_create do + self.reciprocate relation: :flora_relationships, parent_object_ref: :flora, added_object_ref: :related_flora + end + + after_destroy do + # This is a two-way relation, so we should also delete the reverse association + this_object = Flora.find_by(id: self.flora_id) + other_object = Flora.find_by(id: self.related_flora_id) + + other_object.related_floras.delete this_object + end +end diff --git a/app/models/content_types/flora.rb b/app/models/content_types/flora.rb new file mode 100644 index 00000000..636588f7 --- /dev/null +++ b/app/models/content_types/flora.rb @@ -0,0 +1,38 @@ +class Flora < ActiveRecord::Base + validates :name, presence: true + + belongs_to :user + validates :user_id, presence: true + + include BelongsToUniverse + + include HasAttributes + include HasPrivacy + include HasContentGroupers + include HasImageUploads + include HasChangelog + + include Serendipitous::Concern + + include Authority::Abilities + self.authorizer_name = 'ExtendedContentAuthorizer' + + relates :related_floras, with: :flora_relationships + relates :magics, with: :flora_magical_effects + relates :locations, with: :flora_locations + relates :creatures, with: :flora_eaten_by + + scope :is_public, -> { eager_load(:universe).where('floras.privacy = ? OR universes.privacy = ?', 'public', 'public') } + + def self.content_name + 'flora' + end + + def self.color + 'text-lighten-3 lighten-3 teal' + end + + def self.icon + 'local_florist' + end +end diff --git a/app/models/content_types/universe.rb b/app/models/content_types/universe.rb index 6e22fdfc..a5d40178 100644 --- a/app/models/content_types/universe.rb +++ b/app/models/content_types/universe.rb @@ -31,6 +31,7 @@ class Universe < ActiveRecord::Base has_many :religions has_many :magics has_many :languages + has_many :floras has_many :scenes has_many :groups diff --git a/app/views/content/cards/_in_universe_content_list.html.erb b/app/views/content/cards/_in_universe_content_list.html.erb index e2d1e8e1..b796ca46 100644 --- a/app/views/content/cards/_in_universe_content_list.html.erb +++ b/app/views/content/cards/_in_universe_content_list.html.erb @@ -30,7 +30,11 @@ <%= content_type_class.icon %> <% end %> - <%= content_type.to_s.pluralize.titleize %> + <% if defined?(@content) && @content.is_a?(Universe) && content_type != :universe %> + <%= link_to content_type.to_s.pluralize.titleize, send("#{content_type.to_s.pluralize}_universe_path", { id: @content.id }), class: "#{content_type_class.color}-text" %> + <% else %> + <%= content_type.to_s.pluralize.titleize %> + <% end %> close <% if content_list.any? %> @@ -54,6 +58,16 @@ <% end %> +
+ <%= t("content_descriptions.#{@content_type.name.downcase}") %> +
+