mirror of
https://github.com/indentlabs/notebook.git
synced 2025-10-26 11:19:22 +00:00
Convert attribute_categories to system categories
This commit is contained in:
parent
d0f33cbbb0
commit
5a3cfcb6d1
15
app/controllers/attribute_categories_controller.rb
Normal file
15
app/controllers/attribute_categories_controller.rb
Normal file
@ -0,0 +1,15 @@
|
||||
# Controller for the Attribute model
|
||||
class AttributeCategoriesController < ContentController
|
||||
private
|
||||
|
||||
def content_params
|
||||
params.require(:attribute_category).permit(content_param_list)
|
||||
end
|
||||
|
||||
def content_param_list
|
||||
[
|
||||
:user_id, :entity_type,
|
||||
:name, :label, :icon, :description
|
||||
]
|
||||
end
|
||||
end
|
||||
17
app/controllers/attribute_fields_controller.rb
Normal file
17
app/controllers/attribute_fields_controller.rb
Normal file
@ -0,0 +1,17 @@
|
||||
# Controller for the Attribute model
|
||||
class AttributeFieldsController < ContentController
|
||||
private
|
||||
|
||||
def content_params
|
||||
params.require(:attribute_field).permit(content_param_list)
|
||||
end
|
||||
|
||||
def content_param_list
|
||||
[
|
||||
:universe_id, :user_id,
|
||||
:attribute_category_id,
|
||||
:name, :field_type,
|
||||
:label, :description
|
||||
]
|
||||
end
|
||||
end
|
||||
@ -17,6 +17,7 @@ class CharactersController < ContentController
|
||||
:mannerisms, :birthday, :education, :background,
|
||||
:fave_color, :fave_food, :fave_possession, :fave_weapon, :fave_animal,
|
||||
:notes, :private_notes, :privacy,
|
||||
custom_attributes: [:name, :value],
|
||||
siblingships_attributes: [:id, :sibling_id, :_destroy],
|
||||
fatherships_attributes: [:id, :father_id, :_destroy],
|
||||
motherships_attributes: [:id, :mother_id, :_destroy],
|
||||
|
||||
@ -20,7 +20,7 @@ module HasOwnership
|
||||
model = content_type_from_controller(self.class)
|
||||
redirect_if_not_owned model.find(params[:id]), send(redirect_path)
|
||||
rescue
|
||||
redirect_to '/500'
|
||||
redirect_to '/500' unless Rails.env.development?
|
||||
end
|
||||
|
||||
# Unless this content is shared, ensure only the owner can do this action
|
||||
|
||||
@ -23,8 +23,10 @@ class ContentController < ApplicationController
|
||||
|
||||
def show
|
||||
# TODO: Secure this with content class whitelist lel
|
||||
@content = content_type_from_controller(self.class).find(params[:id])
|
||||
content_type = content_type_from_controller(self.class)
|
||||
@content = content_type.find(params[:id])
|
||||
@question = @content.question if current_user.present? and current_user == @content.user
|
||||
@attribute_categories = current_user.attribute_categories.joins(:attribute_fields).where(['attribute_categories.entity_type = ?', content_type])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # show.html.erb
|
||||
@ -33,8 +35,9 @@ class ContentController < ApplicationController
|
||||
end
|
||||
|
||||
def new
|
||||
@content = content_type_from_controller(self.class)
|
||||
.new
|
||||
content_type = content_type_from_controller(self.class)
|
||||
@content = content_type.new
|
||||
@attribute_categories = current_user.attribute_categories.joins(:attribute_fields).where(['attribute_categories.entity_type = ?', content_type])
|
||||
|
||||
respond_to do |format|
|
||||
format.html # new.html.erb
|
||||
@ -43,8 +46,9 @@ class ContentController < ApplicationController
|
||||
end
|
||||
|
||||
def edit
|
||||
@content = content_type_from_controller(self.class)
|
||||
.find(params[:id])
|
||||
content_type = content_type_from_controller(self.class)
|
||||
@content = content_type.find(params[:id])
|
||||
@attribute_categories = current_user.attribute_categories.joins(:attribute_fields).where(['attribute_categories.entity_type = ?', content_type])
|
||||
end
|
||||
|
||||
def create
|
||||
@ -81,6 +85,7 @@ class ContentController < ApplicationController
|
||||
|
||||
def initialize_object
|
||||
content_type = content_type_from_controller(self.class)
|
||||
|
||||
@content = content_type.new(content_params).tap do |c|
|
||||
c.user_id = current_user.id
|
||||
end
|
||||
|
||||
8
app/models/attribute.rb
Normal file
8
app/models/attribute.rb
Normal file
@ -0,0 +1,8 @@
|
||||
class Attribute < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :attribute_field
|
||||
belongs_to :entity, polymorphic: true
|
||||
|
||||
include HasPrivacy
|
||||
scope :is_public, -> { eager_load(:universe).where('universes.privacy = ? OR attributes.privacy = ?', 'public', 'public') }
|
||||
end
|
||||
27
app/models/attribute_category.rb
Normal file
27
app/models/attribute_category.rb
Normal file
@ -0,0 +1,27 @@
|
||||
class AttributeCategory < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
has_many :attribute_fields
|
||||
|
||||
include Serendipitous::Concern
|
||||
|
||||
def self.color
|
||||
'amber'
|
||||
end
|
||||
|
||||
def self.icon
|
||||
'chrome_reader_mode'
|
||||
end
|
||||
|
||||
def self.content_name
|
||||
'attribute category'
|
||||
end
|
||||
|
||||
def self.attribute_categories
|
||||
{
|
||||
general: {
|
||||
icon: 'info',
|
||||
attributes: %w(name label icon entity_type)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
38
app/models/attribute_field.rb
Normal file
38
app/models/attribute_field.rb
Normal file
@ -0,0 +1,38 @@
|
||||
class AttributeField < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
|
||||
include HasAttributes
|
||||
include Serendipitous::Concern
|
||||
|
||||
attr_accessor :system
|
||||
|
||||
scope :is_public, -> { eager_load(:universe).where('universes.privacy = ? OR attribute_fields.privacy = ?', 'public', 'public') }
|
||||
|
||||
def self.color
|
||||
'amber'
|
||||
end
|
||||
|
||||
def self.icon
|
||||
'text_fields'
|
||||
end
|
||||
|
||||
def self.content_name
|
||||
'attribute'
|
||||
end
|
||||
|
||||
def name
|
||||
(self['name'] || "custom field #{Time.now.to_i}").downcase.gsub(' ','_')
|
||||
end
|
||||
|
||||
def humanize
|
||||
label
|
||||
end
|
||||
|
||||
def private?
|
||||
privacy != 'public'
|
||||
end
|
||||
|
||||
def system?
|
||||
!!self.system
|
||||
end
|
||||
end
|
||||
@ -14,6 +14,7 @@ class Character < ActiveRecord::Base
|
||||
|
||||
belongs_to :universe
|
||||
|
||||
include HasAttributes
|
||||
include HasPrivacy
|
||||
include HasContentGroupers
|
||||
include Serendipitous::Concern
|
||||
@ -33,12 +34,18 @@ class Character < ActiveRecord::Base
|
||||
# Items
|
||||
relates :favorite_items, with: :ownerships, where: { favorite: true }
|
||||
|
||||
has_many :custom_attributes
|
||||
|
||||
scope :is_public, -> { eager_load(:universe).where('characters.privacy = ? OR universes.privacy = ?', 'public', 'public') }
|
||||
|
||||
def description
|
||||
role
|
||||
end
|
||||
|
||||
def self.content_name
|
||||
'character'
|
||||
end
|
||||
|
||||
def self.color
|
||||
'red'
|
||||
end
|
||||
@ -46,38 +53,4 @@ class Character < ActiveRecord::Base
|
||||
def self.icon
|
||||
'group'
|
||||
end
|
||||
|
||||
def self.attribute_categories
|
||||
{
|
||||
overview: {
|
||||
icon: 'info',
|
||||
attributes: %w(name role gender age universe_id)
|
||||
},
|
||||
looks: {
|
||||
icon: 'face',
|
||||
attributes: %w(weight height haircolor hairstyle facialhair eyecolor race skintone bodytype identmarks)
|
||||
},
|
||||
social: {
|
||||
icon: 'groups',
|
||||
attributes: %w(best_friends archenemies religion politics prejudices occupation)
|
||||
},
|
||||
# TODO: remove schema for mannerisms
|
||||
history: {
|
||||
icon: 'info',
|
||||
attributes: %w(birthday birthplaces education background)
|
||||
},
|
||||
faves: {
|
||||
icon: 'star',
|
||||
attributes: %w(fave_color fave_food fave_possession fave_weapon fave_animal)
|
||||
},
|
||||
family: {
|
||||
icon: 'face',
|
||||
attributes: %w(mothers fathers spouses siblings children)
|
||||
},
|
||||
notes: {
|
||||
icon: 'edit',
|
||||
attributes: %w(notes private_notes)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
27
app/models/concerns/has_attributes.rb
Normal file
27
app/models/concerns/has_attributes.rb
Normal file
@ -0,0 +1,27 @@
|
||||
require 'active_support/concern'
|
||||
|
||||
module HasAttributes
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
attr_accessor :attribute_values
|
||||
after_save :update_attribute_values
|
||||
|
||||
def self.attribute_categories
|
||||
YAML.load_file(Rails.root.join('config', 'attributes', "#{content_name}.yml")).map do |category_name, details|
|
||||
category = AttributeCategory.new(entity_type: self.name, name: category_name.to_s, label: details[:label], icon: details[:icon])
|
||||
category.attribute_fields << details[:attributes].map { |field| AttributeField.new(field.merge(system: true)) }
|
||||
category
|
||||
end
|
||||
end
|
||||
|
||||
def update_attribute_values
|
||||
(self.attribute_values || []).each do |attribute|
|
||||
field = user.attribute_fields.find_by_name(attribute[:name])
|
||||
next if field.nil?
|
||||
|
||||
user.custom_attributes.where(entity: self, attribute_field_id: field.id).first_or_create(value: attribute[:value])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -12,6 +12,7 @@ class Item < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :universe
|
||||
|
||||
include HasAttributes
|
||||
include HasPrivacy
|
||||
include HasContentGroupers
|
||||
include Serendipitous::Concern
|
||||
@ -31,28 +32,7 @@ class Item < ActiveRecord::Base
|
||||
'beach_access'
|
||||
end
|
||||
|
||||
def self.attribute_categories
|
||||
{
|
||||
overview: {
|
||||
icon: 'info',
|
||||
attributes: %w(name item_type description universe_id)
|
||||
},
|
||||
looks: {
|
||||
icon: 'redeem',
|
||||
attributes: %w(materials weight)
|
||||
},
|
||||
history: {
|
||||
icon: 'book',
|
||||
attributes: %w(original_owners current_owners makers year_made)
|
||||
},
|
||||
abilities: {
|
||||
icon: 'flash_on',
|
||||
attributes: %w(magic)
|
||||
},
|
||||
notes: {
|
||||
icon: 'edit',
|
||||
attributes: %w(notes private_notes)
|
||||
}
|
||||
}
|
||||
def self.content_name
|
||||
'item'
|
||||
end
|
||||
end
|
||||
|
||||
@ -15,6 +15,7 @@ class Location < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :universe
|
||||
|
||||
include HasAttributes
|
||||
include HasPrivacy
|
||||
include HasContentGroupers
|
||||
include Serendipitous::Concern
|
||||
@ -37,33 +38,7 @@ class Location < ActiveRecord::Base
|
||||
'green'
|
||||
end
|
||||
|
||||
def self.attribute_categories
|
||||
{
|
||||
overview: {
|
||||
icon: 'info',
|
||||
attributes: %w(name type_of description universe_id)
|
||||
},
|
||||
# TODO: map
|
||||
culture: {
|
||||
icon: 'face',
|
||||
attributes: %w(leaders population language currency motto)
|
||||
},
|
||||
cities: {
|
||||
icon: 'business',
|
||||
attributes: %w(capital_cities largest_cities notable_cities)
|
||||
},
|
||||
geography: {
|
||||
icon: 'map',
|
||||
attributes: %w(area crops located_at)
|
||||
},
|
||||
history: {
|
||||
icon: 'book',
|
||||
attributes: %w(established_year notable_wars)
|
||||
},
|
||||
notes: {
|
||||
icon: 'edit',
|
||||
attributes: %w(notes private_notes)
|
||||
}
|
||||
}
|
||||
def self.content_name
|
||||
'location'
|
||||
end
|
||||
end
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
#
|
||||
# contains all canonically-related content created by Users
|
||||
class Universe < ActiveRecord::Base
|
||||
|
||||
include HasAttributes
|
||||
include HasPrivacy
|
||||
include Serendipitous::Concern
|
||||
|
||||
@ -15,6 +17,7 @@ class Universe < ActiveRecord::Base
|
||||
has_many :characters
|
||||
has_many :items
|
||||
has_many :locations
|
||||
has_many :attribute_fields
|
||||
|
||||
scope :is_public, -> { where(privacy: 'public') }
|
||||
|
||||
@ -34,24 +37,7 @@ class Universe < ActiveRecord::Base
|
||||
'vpn_lock'
|
||||
end
|
||||
|
||||
def self.attribute_categories
|
||||
{
|
||||
overview: {
|
||||
icon: 'info',
|
||||
attributes: %w(name description)
|
||||
},
|
||||
history: {
|
||||
icon: 'book',
|
||||
attributes: %w(history)
|
||||
},
|
||||
notes: {
|
||||
icon: 'edit',
|
||||
attributes: %w(notes private_notes)
|
||||
},
|
||||
settings: {
|
||||
icon: 'build',
|
||||
attributes: %w(privacy)
|
||||
}
|
||||
}
|
||||
def self.content_name
|
||||
'universe'
|
||||
end
|
||||
end
|
||||
|
||||
@ -15,6 +15,9 @@ class User < ActiveRecord::Base
|
||||
has_many :items
|
||||
has_many :locations
|
||||
has_many :universes
|
||||
has_many :attribute_fields
|
||||
has_many :attribute_categories
|
||||
has_many :custom_attributes, class_name: 'Attribute'
|
||||
|
||||
# as_json creates a hash structure, which you then pass to ActiveSupport::json.encode to actually encode the object as a JSON string.
|
||||
# This is different from to_json, which converts it straight to an escaped JSON string,
|
||||
|
||||
20
app/views/admin/attributes.html.erb
Normal file
20
app/views/admin/attributes.html.erb
Normal file
@ -0,0 +1,20 @@
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>Attributes</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col s4">
|
||||
<h2>Characters per user</h1>
|
||||
<%= column_chart User.joins(:attribute_fields).group(:user_id).count().group_by { |n| n.last }.each_with_object({}) { |(content_count, ids), h| h[content_count] = ids.count } %>
|
||||
</div>
|
||||
<div class="col s4">
|
||||
<h2>Attributes per universe</h1>
|
||||
<%= column_chart Universe.joins(:attribute_fields).group(:universe_id).count().group_by { |n| n.last }.each_with_object({}) { |(content_count, ids), h| h[content_count] = ids.count } %>
|
||||
</div>
|
||||
<div class="col s4">
|
||||
<h2>Attribute privacy</h1>
|
||||
<%= pie_chart AttributeField.where.not(privacy: "").group(:privacy).count() %>
|
||||
</div>
|
||||
</div>
|
||||
31
app/views/attribute_categories/_list.html.erb
Normal file
31
app/views/attribute_categories/_list.html.erb
Normal file
@ -0,0 +1,31 @@
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Entity</th>
|
||||
<th>Description</th>
|
||||
<% if session[:user] %>
|
||||
<th><%=t '.actions', :default => t("helpers.actions") %></th>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @content.each do |category| %>
|
||||
<tr>
|
||||
<td style="width: 160px;"><%= simple_format link_to category.label, attribute_category_path(category) %></td>
|
||||
<td><%= simple_format category.entity_type.capitalize %></td>
|
||||
<td><%= simple_format category.description %></td>
|
||||
<td style="width: 240px;">
|
||||
<% if session[:user] and session[:user] == category.user.id %>
|
||||
<%= link_to t('.view', :default => t("helpers.links.view")),
|
||||
attribute_field_path(category), :class => 'btn' %>
|
||||
<%= link_to t('.edit', :default => t("helpers.links.edit")),
|
||||
attribute_field_edit_path(category), :class => 'btn' %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
3
app/views/attribute_categories/edit.html.erb
Normal file
3
app/views/attribute_categories/edit.html.erb
Normal file
@ -0,0 +1,3 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
3
app/views/attribute_categories/new.html.erb
Normal file
3
app/views/attribute_categories/new.html.erb
Normal file
@ -0,0 +1,3 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
12
app/views/attribute_categories/show.html.erb
Normal file
12
app/views/attribute_categories/show.html.erb
Normal file
@ -0,0 +1,12 @@
|
||||
<script type="application/ld+json">
|
||||
<%
|
||||
content_jsonld = {
|
||||
'@id': item_url,
|
||||
'@type': 'http://schema.org/Thing',
|
||||
'http://schema.org/name': @content.name
|
||||
}
|
||||
%>
|
||||
<%= content_jsonld.to_json.html_safe %>
|
||||
</script>
|
||||
|
||||
<%= render partial: 'content/show', locals: { content: @content } %>
|
||||
31
app/views/attribute_fields/_list.html.erb
Normal file
31
app/views/attribute_fields/_list.html.erb
Normal file
@ -0,0 +1,31 @@
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<% if session[:user] %>
|
||||
<th><%=t '.actions', :default => t("helpers.actions") %></th>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @content.each do |field| %>
|
||||
<tr>
|
||||
<td style="width: 160px;"><%= simple_format link_to field.label, attribute_field_path(field) %></td>
|
||||
<td><%= simple_format field.field_type %></td>
|
||||
<td><%= simple_format field.description %></td>
|
||||
<td style="width: 240px;">
|
||||
<% if session[:user] and session[:user] == field.user.id %>
|
||||
<%= link_to t('.view', :default => t("helpers.links.view")),
|
||||
attribute_field_path(field), :class => 'btn' %>
|
||||
<%= link_to t('.edit', :default => t("helpers.links.edit")),
|
||||
attribute_field_edit_path(field), :class => 'btn' %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
3
app/views/attribute_fields/edit.html.erb
Normal file
3
app/views/attribute_fields/edit.html.erb
Normal file
@ -0,0 +1,3 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
3
app/views/attribute_fields/new.html.erb
Normal file
3
app/views/attribute_fields/new.html.erb
Normal file
@ -0,0 +1,3 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
12
app/views/attribute_fields/show.html.erb
Normal file
12
app/views/attribute_fields/show.html.erb
Normal file
@ -0,0 +1,12 @@
|
||||
<script type="application/ld+json">
|
||||
<%
|
||||
content_jsonld = {
|
||||
'@id': item_url,
|
||||
'@type': 'http://schema.org/Thing',
|
||||
'http://schema.org/name': @content.label
|
||||
}
|
||||
%>
|
||||
<%= content_jsonld.to_json.html_safe %>
|
||||
</script>
|
||||
|
||||
<%= render partial: 'content/show', locals: { content: @content } %>
|
||||
@ -15,129 +15,36 @@
|
||||
|
||||
<%# Tabs %>
|
||||
<ul class="hoverable tabs hide-on-small-only">
|
||||
<% content.class.attribute_categories.each do |category, data| %>
|
||||
<% content.class.attribute_categories.each do |category| %>
|
||||
<li class="tab col s12">
|
||||
<a href="#<%= category %>">
|
||||
<i class="material-icons hide-on-med-and-down" style="font-size: 18px; position: relative; top: 3px;"><%= data[:icon] %></i>
|
||||
<%= category.to_s.humanize %>
|
||||
<a href="#<%= category.name %>_panel">
|
||||
<i class="material-icons hide-on-med-and-down" style="font-size: 18px; position: relative; top: 3px;"><%= category.icon %></i>
|
||||
<%= category.label %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<ul class="tabs hide-on-med-and-up">
|
||||
<% content.class.attribute_categories.each do |category, data| %>
|
||||
<% content.class.attribute_categories.each do |category| %>
|
||||
<li class="tab col s12">
|
||||
<a href="#<%= category %>" class="tooltipped" data-position="bottom" data-delay="100" data-tooltip="<%= category.to_s.humanize %>">
|
||||
<i class="material-icons" style="font-size: 18px; position: relative; top: 3px;"><%= data[:icon] %></i>
|
||||
<a href="#<%= category.name %>_panel" class="tooltipped" data-position="bottom" data-delay="100" data-tooltip="<%= category.label %>">
|
||||
<i class="material-icons" style="font-size: 18px; position: relative; top: 3px;"><%= category.icon %></i>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<br />
|
||||
|
||||
<%# Content panels %>
|
||||
<% content.class.attribute_categories.each do |category, data| %>
|
||||
<div id="<%= category %>" class="row">
|
||||
<% data[:attributes].each do |attribute| %>
|
||||
|
||||
<% # Do some dynamic resizing of columns based on how many columns there are
|
||||
# TODO: move this into some service or something? Dunno, doesn't belong here.
|
||||
|
||||
s_width = 12
|
||||
m_width = 6
|
||||
l_width = 4
|
||||
|
||||
if data[:attributes].length == 1
|
||||
# If there's only one field on this tab, go full-width on all screen sizes
|
||||
s_width = m_width = l_width = 12
|
||||
elsif data[:attributes].length == 2
|
||||
# If there's two fields on this tab, go half-width on medium- and large-screens
|
||||
s_width = 12
|
||||
m_width = l_width = 6
|
||||
elsif data[:attributes].length > 2
|
||||
# If there's at least 3 fields, use the defaults (detailed above)
|
||||
end
|
||||
%>
|
||||
|
||||
<div class="col <%= "s#{s_width} m#{m_width} l#{l_width}" %>">
|
||||
|
||||
<% value = content.send(attribute) %>
|
||||
<% if value.is_a?(ActiveRecord::Associations::CollectionProxy) %>
|
||||
<%# Relation-setting UI %>
|
||||
<% through_class = content.class.reflect_on_association(attribute).options[:through].to_s %>
|
||||
<%= render 'content/form/relation_input', f: f, attribute: attribute, relation: through_class %>
|
||||
|
||||
<% elsif attribute == 'universe_id' %>
|
||||
<div class="input-field">
|
||||
<%= f.label attribute %><br />
|
||||
<%= f.select attribute, current_user.universes.sort_by(&:name).map { |u| [u.name, u.id] }.compact, include_blank: true, selected: (f.object.persisted? ? f.object.universe_id : session[:universe_id]) %>
|
||||
</div>
|
||||
|
||||
<% elsif attribute == 'privacy' %>
|
||||
<div class="input-field">
|
||||
<%= f.label attribute %><br />
|
||||
<%= f.select attribute, [['Only visible to you', 'private'], ['Visible to anyone with the URL', 'public']] %>
|
||||
<div class="help-text">
|
||||
This setting applies to this universe and everything within it. If this universe is visible to others by URL, they will be able to click through and see the
|
||||
characters, locations, and items within this universe also.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% elsif attribute == 'private_notes' %>
|
||||
<div class="input-field content-field">
|
||||
<%= f.label attribute, attribute.humanize.capitalize %>
|
||||
<%= f.text_area attribute, class: "materialize-textarea", placeholder: 'Write as little or as much as you want!' %>
|
||||
<div class="help-text">
|
||||
Private notes are <em>always</em> visible to only you, even if content is made public and shared.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% elsif attribute == 'item_type' %>
|
||||
<% potential_item_types = t('weapon_types') + t('shield_types') + t('axe_types') + t('bow_types') +
|
||||
t('club_types') + t('flexible_weapon_types') + t('fist_weapon_types') + t('thrown_weapon_types') +
|
||||
t('polearm_types') + t('shortsword_types') + t('sword_types') + t('other_item_types')
|
||||
%>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute, autocomplete: potential_item_types %>
|
||||
|
||||
<%# TODO: not make this so awful %>
|
||||
|
||||
<% elsif attribute == 'skintone' %>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute, autocomplete: t('skin_tones') %>
|
||||
|
||||
<% elsif attribute == 'race' %>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute, autocomplete: t('character_races') %>
|
||||
|
||||
<% elsif attribute == 'eyecolor' %>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute, autocomplete: t('eye_colors') %>
|
||||
|
||||
<% elsif attribute == 'haircolor' %>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute, autocomplete: t('hair_colors') %>
|
||||
|
||||
<% elsif attribute == 'hairstyle' %>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute, autocomplete: t('hair_styles') %>
|
||||
|
||||
<% elsif attribute == 'facialhair' %>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute, autocomplete: t('facial_hair_styles') %>
|
||||
|
||||
<% elsif attribute == 'bodytype' %>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute, autocomplete: t('body_types') %>
|
||||
|
||||
<% elsif attribute == 'fave_weapon' %>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute, autocomplete: t('weapon_types') + t('shield_types') + t('axe_types') + t('bow_types') +
|
||||
t('club_types') + t('flexible_weapon_types') + t('fist_weapon_types') + t('thrown_weapon_types') +
|
||||
t('polearm_types') + t('shortsword_types') + t('sword_types') %>
|
||||
|
||||
<% else %>
|
||||
<%= render 'content/form/text_input', f: f, attribute: attribute %>
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render 'content/form/panel', category: category, f: f, content: content %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="card-action center">
|
||||
<%= f.submit nil, class: "btn waves-effect waves-#{content.class.color}" %>
|
||||
<% action = content.new_record? ? 'Create' : 'Update' %>
|
||||
<%= f.submit "#{action} #{content.class.content_name}", class: "btn waves-effect waves-#{content.class.color}" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<% if @content.present? && @content.respond_to?(:as_jsonld) %>
|
||||
<script type="application/ld+json">
|
||||
<%= @content.as_jsonld.to_json.html_safe %>
|
||||
<%= @content.as_jsonld.to_json.html_safe %>
|
||||
</script>
|
||||
<% end %>
|
||||
|
||||
@ -8,8 +8,6 @@
|
||||
set_meta_tags title: content.name, description: content.description
|
||||
%>
|
||||
|
||||
|
||||
|
||||
<% content_for :sidebar_top do %>
|
||||
<%= render partial: 'cards/serendipitous/content_question', locals: { question: @question, content: @content } %>
|
||||
<% end %>
|
||||
@ -24,19 +22,19 @@ set_meta_tags title: content.name, description: content.description
|
||||
<%= content.name %>
|
||||
</div>
|
||||
<ul class="tabs" style="margin-bottom:30px;">
|
||||
<% content.class.attribute_categories.each do |category, data| %>
|
||||
<li class="tab col s3 <%= "disabled" unless data[:attributes].any? { |m| content.send(m).present? } %>"><a href="#<%= category %>"><%= category.to_s.humanize %></a></li>
|
||||
<% content.class.attribute_categories.each do |category| %>
|
||||
<li class="tab col s3 <%= "disabled" unless category.attribute_fields.map(&:name).any? { |m| content.send(m).present? } %>"><a href="#<%= category.name %>_panel"><%= category.label %></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% content.class.attribute_categories.each do |category, data| %>
|
||||
<div id="<%= category %>" class="row">
|
||||
<% data[:attributes].each do |attribute| %>
|
||||
<% next if attribute.start_with?("private") && @content.user != current_user %>
|
||||
<% next if content.send(attribute).blank? %>
|
||||
<div class="row">
|
||||
<div class="col s3 m3 l2 right-align flow-text grey-text"><%= attribute.humanize.capitalize %></div>
|
||||
<% content.class.attribute_categories.each do |category| %>
|
||||
<div id="<%= category.name %>_panel" class="row">
|
||||
<% category.attribute_fields.each do |attribute| %>
|
||||
<% next if attribute.name.start_with?("private") && @content.user != current_user %>
|
||||
<% value = content.send(attribute.name) %>
|
||||
<% next if value.blank? %>
|
||||
|
||||
<% value = content.send(attribute) %>
|
||||
<div class="row">
|
||||
<div class="col s3 m3 l2 right-align flow-text grey-text"><%= attribute.label %></div>
|
||||
<% if value.is_a?(ActiveRecord::Associations::CollectionProxy) %>
|
||||
<% klass = value.first.class || value.build.class %>
|
||||
<div class="col s9 m9 l10 flow-text">
|
||||
@ -49,7 +47,7 @@ set_meta_tags title: content.name, description: content.description
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% elsif attribute == 'universe_id' %>
|
||||
<% elsif attribute.name == 'universe_id' %>
|
||||
<div class="col s9 m9 l10 flow-text">
|
||||
<%= link_to content.universe.name, content.universe if content.universe %>
|
||||
</div>
|
||||
|
||||
107
app/views/content/form/_panel.html.erb
Normal file
107
app/views/content/form/_panel.html.erb
Normal file
@ -0,0 +1,107 @@
|
||||
<div id="<%= category.name %>_panel" class="row">
|
||||
<% category.attribute_fields.each do |field| %>
|
||||
|
||||
<% # Do some dynamic resizing of columns based on how many columns there are
|
||||
# TODO: move this into some service or something? Dunno, doesn't belong here.
|
||||
s_width = 12
|
||||
m_width = 6
|
||||
l_width = 4
|
||||
if category.attribute_fields.length == 1
|
||||
# If there's only one field on this tab, go full-width on all screen sizes
|
||||
s_width = m_width = l_width = 12
|
||||
elsif category.attribute_fields.length == 2
|
||||
# If there's two fields on this tab, go half-width on medium- and large-screens
|
||||
s_width = 12
|
||||
m_width = l_width = 6
|
||||
elsif category.attribute_fields.length > 2
|
||||
# If there's at least 3 fields, use the defaults (detailed above)
|
||||
end
|
||||
%>
|
||||
|
||||
<div class="col <%= "s#{s_width} m#{m_width} l#{l_width}" %>">
|
||||
|
||||
<% value = content.send(field.name.to_sym) %>
|
||||
<% if value.is_a?(ActiveRecord::Associations::CollectionProxy) %>
|
||||
<%# Relation-setting UI %>
|
||||
<% through_class = content.class.reflect_on_association(field.name).options[:through].to_s %>
|
||||
<%= render 'content/form/relation_input', f: f, attribute: field.name, relation: through_class %>
|
||||
|
||||
<% elsif field.name == 'universe_id' %>
|
||||
<div class="input-field">
|
||||
<%= f.label field.name, field.label %><br />
|
||||
<%= f.select field.name, current_user.universes.sort_by(&:name).map { |u| [u.name, u.id] }.compact, include_blank: true, selected: (f.object.persisted? ? f.object.universe_id : session[:universe_id]) %>
|
||||
</div>
|
||||
|
||||
<% elsif field.name == 'attribute_category_id' %>
|
||||
<div class="input-field">
|
||||
<%= f.label field.name, field.label %><br />
|
||||
<%= f.select field.name, current_user.attribute_categories.sort_by(&:label).map { |u| [u.label, u.id] }.compact, include_blank: false, selected: (f.object.persisted? ? f.object.attribute_category_id : nil) %>
|
||||
</div>
|
||||
|
||||
<% elsif field.name == 'field_type' %>
|
||||
<div class="input-field">
|
||||
<%= f.label field.name, field.label %><br />
|
||||
<%= f.select field.name, ['Text Field'].map { |tf| [tf, tf.underscore] } %>
|
||||
</div>
|
||||
|
||||
<% elsif field.name == 'privacy' %>
|
||||
<div class="input-field">
|
||||
<%= f.label field.name, field.label %><br />
|
||||
<%= f.select field.name, [['Only visible to you', 'private'], ['Visible to anyone with the URL', 'public']] %>
|
||||
<div class="help-text">
|
||||
This setting applies to this universe and everything within it. If this universe is visible to others by URL, they will be able to click through and see the
|
||||
characters, locations, and items within this universe also.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% elsif field.name == 'private_notes' %>
|
||||
<div class="input-field content-field">
|
||||
<%= f.label field.name, field.label %>
|
||||
<%= f.text_area field.name, class: "materialize-textarea", placeholder: 'Write as little or as much as you want!' %>
|
||||
<div class="help-text">
|
||||
Private notes are <em>always</em> visible to only you, even if content is made public and shared.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% elsif field.name == 'item_type' %>
|
||||
<% potential_item_types = t('weapon_types') + t('shield_types') + t('axe_types') + t('bow_types') +
|
||||
t('club_types') + t('flexible_weapon_types') + t('fist_weapon_types') + t('thrown_weapon_types') +
|
||||
t('polearm_types') + t('shortsword_types') + t('sword_types') + t('other_item_types')
|
||||
%>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field, autocomplete: potential_item_types %>
|
||||
|
||||
<%# TODO: not make this so awful %>
|
||||
|
||||
<% elsif field.name == 'skintone' %>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field, autocomplete: t('skin_tones') %>
|
||||
|
||||
<% elsif field.name == 'race' %>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field, autocomplete: t('character_races') %>
|
||||
|
||||
<% elsif field.name == 'eyecolor' %>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field, autocomplete: t('eye_colors') %>
|
||||
|
||||
<% elsif field.name == 'haircolor' %>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field, autocomplete: t('hair_colors') %>
|
||||
|
||||
<% elsif field.name == 'hairstyle' %>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field, autocomplete: t('hair_styles') %>
|
||||
|
||||
<% elsif field.name == 'facialhair' %>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field, autocomplete: t('facial_hair_styles') %>
|
||||
|
||||
<% elsif field.name == 'bodytype' %>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field, autocomplete: t('body_types') %>
|
||||
|
||||
<% elsif field.name == 'fave_weapon' %>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field, autocomplete: t('weapon_types') + t('shield_types') + t('axe_types') + t('bow_types') +
|
||||
t('club_types') + t('flexible_weapon_types') + t('fist_weapon_types') + t('thrown_weapon_types') +
|
||||
t('polearm_types') + t('shortsword_types') + t('sword_types') %>
|
||||
|
||||
<% else %>
|
||||
<%= render 'content/form/text_input', f: f, content: content, field: field %>
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -1,19 +1,23 @@
|
||||
<%
|
||||
field_id = "#{f.object.class.name.downcase}_#{attribute}"
|
||||
|
||||
value = content.send(field.name.to_sym)
|
||||
# TODO: Enable autocomplete when we can actually hide the dropdown if someone tabs out of a field.
|
||||
# Not the easiest thing in the world to do, apparently.
|
||||
%>
|
||||
|
||||
<div class="input-field content-field">
|
||||
<%= f.label attribute, attribute.humanize.capitalize %>
|
||||
<%= f.text_area attribute, class: "materialize-textarea #{defined?(autocomplete) && false ? 'autocomplete' : ''}", placeholder: 'Write as little or as much as you want!' %>
|
||||
<%= f.label field.name, field.label %>
|
||||
<% if field.system? %>
|
||||
<%= f.text_area field.name, value: value, class: "materialize-textarea #{defined?(autocomplete) && false ? 'autocomplete' : ''}", placeholder: 'Write as little or as much as you want!' %>
|
||||
<% else %>
|
||||
<%= hidden_field_tag "#{content.content_name}[custom_attributes][][name]", field.name %>
|
||||
<%= text_area_tag "#{content.content_name}[custom_attributes][][value]", value, class: "materialize-textarea #{defined?(autocomplete) && false ? 'autocomplete' : ''}", placeholder: 'Write as little or as much as you want!' %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if defined?(autocomplete) && false %>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('#<%= field_id %>').autocomplete({
|
||||
$('#<%= "#{content.content_name}_#{field.name}" %>').autocomplete({
|
||||
data: {
|
||||
<% autocomplete.each do |autocomplete_option| %>
|
||||
"<%= autocomplete_option %>": null,
|
||||
@ -22,4 +26,4 @@
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<% if content.persisted? %>
|
||||
<%= content.name %>
|
||||
<% else %>
|
||||
Create your new <%= content.class.to_s.downcase %>
|
||||
<% end %>
|
||||
<%- content_name = content.class.respond_to?(:content_name) ? content.class.content_name: content.class.to_s %>
|
||||
Create your new <%= content_name.downcase %>
|
||||
<% end %>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<%
|
||||
content_type_class = @content.build.class
|
||||
content_type = content_type_class.name.downcase
|
||||
%>
|
||||
|
||||
<% content_for :sidebar_top do %>
|
||||
@ -8,22 +7,22 @@
|
||||
<% end %>
|
||||
|
||||
<% if @content.any? %>
|
||||
<h4>You've created <%= pluralize(@content.count, content_type) %></h4>
|
||||
<h4>You've created <%= pluralize(@content.count, @content.content_name) %></h4>
|
||||
<%= render partial: 'content/list/list', locals: { content_list: @content } %>
|
||||
<%= link_to "Create another #{content_type}", new_polymorphic_path(@content.build), :class => 'btn' %>
|
||||
<%= link_to "Create another #{@content.content_name}", new_polymorphic_path(@content.build), :class => 'btn' %>
|
||||
|
||||
<% elsif @content.empty? %>
|
||||
<div class="center">
|
||||
<h4>You haven't created any <%= content_type.pluralize %> yet!</h4>
|
||||
<h4>You haven't created any <%= @content.content_name.pluralize %> yet!</h4>
|
||||
<h1>
|
||||
<i class="material-icons <%= content_type_class.color %>-text" style="font-size: 200%">
|
||||
<%= content_type_class.icon %>
|
||||
</i>
|
||||
</h1>
|
||||
<p>
|
||||
<%= t("content_descriptions.#{content_type}") %>
|
||||
<%= t("content_descriptions.#{content_type_class.name.downcase}") %>
|
||||
</p>
|
||||
<%= link_to "Create your first #{content_type}", new_polymorphic_path(@content.build), :class => 'btn' %>
|
||||
<%= link_to "Create your first #{@content.content_name}", new_polymorphic_path(@content.build), :class => 'btn' %>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
<% if title.present? %>
|
||||
<li class="collection-header"><h4><%= title %></h4></li>
|
||||
<% end %>
|
||||
|
||||
<% content_list.each do |content| %>
|
||||
<li class="collection-item avatar">
|
||||
<i class="material-icons circle <%= content.class.color %>"><%= content.class.icon %></i>
|
||||
@ -39,4 +40,4 @@
|
||||
</span>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
<li><%= link_to 'Your characters', characters_path %></li>
|
||||
<li><%= link_to 'Your locations', locations_path %></li>
|
||||
<li><%= link_to 'Your items', items_path %></li>
|
||||
<li><%= link_to 'Your attributes', attribute_fields_path %></li>
|
||||
<li class="divider"></li>
|
||||
<li><%= link_to 'Your author profile', current_user if current_user %></li>
|
||||
<li><%= link_to 'Account settings', edit_user_registration_path %></li>
|
||||
@ -75,6 +76,11 @@
|
||||
<i class="material-icons">beach_access</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="<%= attribute_fields_url %>" class="tooltipped" data-position="bottom" data-delay="100" data-tooltip="Attributes">
|
||||
<i class="material-icons">assignment</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-button tooltipped" href="#!" data-activates="desktop-user-dropdown" data-position="bottom" data-delay="100" data-tooltip="You">
|
||||
<i class="material-icons">person</i>
|
||||
@ -108,4 +114,4 @@
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
<li><%= link_to "Character stats", admin_characters_path %></li>
|
||||
<li><%= link_to "Location stats", admin_locations_path %></li>
|
||||
<li><%= link_to "Item stats", admin_items_path %></li>
|
||||
<li><%= link_to "Attribute stats", admin_attributes_path %></li>
|
||||
</ul>
|
||||
|
||||
<%= yield %>
|
||||
|
||||
14
config/attributes/attribute.yml
Normal file
14
config/attributes/attribute.yml
Normal file
@ -0,0 +1,14 @@
|
||||
:general:
|
||||
:label: General
|
||||
:icon: info
|
||||
:attributes:
|
||||
- :name: label
|
||||
:label: Label
|
||||
- :name: field_type
|
||||
:label: Field Type
|
||||
- :name: attribute_category_id
|
||||
:label: Attribute Category
|
||||
- :name: name
|
||||
:label: Name
|
||||
- :name: description
|
||||
:label: Description
|
||||
103
config/attributes/character.yml
Normal file
103
config/attributes/character.yml
Normal file
@ -0,0 +1,103 @@
|
||||
:overview:
|
||||
:label: Overview
|
||||
:icon: info
|
||||
:attributes:
|
||||
- :name: name
|
||||
:label: Name
|
||||
- :name: role
|
||||
:label: Role
|
||||
- :name: gender
|
||||
:label: Gender
|
||||
- :name: age
|
||||
:label: Age
|
||||
- :name: universe_id
|
||||
:label: Universe
|
||||
:looks:
|
||||
:label: Looks
|
||||
:icon: face
|
||||
:attributes:
|
||||
- :name: weight
|
||||
:label: Weight
|
||||
- :name: height
|
||||
:label: Height
|
||||
- :name: haircolor
|
||||
:label: Hair Color
|
||||
- :name: hairstyle
|
||||
:label: Hair Style
|
||||
- :name: facialhair
|
||||
:label: Facial Hair
|
||||
- :name: eyecolor
|
||||
:label: Eye Color
|
||||
- :name: race
|
||||
:label: Race
|
||||
- :name: skintone
|
||||
:label: Skin Tone
|
||||
- :name: bodytype
|
||||
:label: Body Type
|
||||
- :name: identmarks
|
||||
:label: Identifying Marks
|
||||
:social:
|
||||
:label: Social
|
||||
:icon: groups
|
||||
:attributes:
|
||||
- :name: best_friends
|
||||
:label: Best Friends
|
||||
- :name: archenemies
|
||||
:label: Arch Enemies
|
||||
- :name: religion
|
||||
:label: Religion
|
||||
- :name: politics
|
||||
:label: Politics
|
||||
- :name: prejudices
|
||||
:label: Prejudices
|
||||
- :name: occupation
|
||||
:label: Occupation
|
||||
:history:
|
||||
:label: history
|
||||
:icon: info
|
||||
:attributes:
|
||||
- :name: birthday
|
||||
:label: Birthday
|
||||
- :name: birthplaces
|
||||
:label: Birthplaces
|
||||
- :name: education
|
||||
:label: Education
|
||||
- :name: background
|
||||
:label: Background
|
||||
:faves:
|
||||
:label: Favorites
|
||||
:icon: star
|
||||
:attributes:
|
||||
- :name: fave_color
|
||||
:label: Color
|
||||
- :name: fave_food
|
||||
:label: Food
|
||||
- :name: fave_possession
|
||||
:label: Possession
|
||||
- :name: fave_weapon
|
||||
:label: Weapon
|
||||
- :name: fave_animal
|
||||
:label: Animal
|
||||
:family:
|
||||
:label: Family
|
||||
:icon: face
|
||||
:attributes:
|
||||
- :name: mothers
|
||||
:label: Mothers
|
||||
- :name: fathers
|
||||
:label: Fathers
|
||||
- :name: spouses
|
||||
:label: Spouses
|
||||
- :name: siblings
|
||||
:label: Siblings
|
||||
- :name: children
|
||||
:label: Children
|
||||
:notes:
|
||||
:label: Notes
|
||||
:icon: edit
|
||||
:attributes:
|
||||
- :name: notes
|
||||
:label: Notes
|
||||
- :name: private_notes
|
||||
:label: Private Notes
|
||||
:description: Private notes are <em>always</em> visible to only you, even if content is made public and shared.
|
||||
46
config/attributes/item.yml
Normal file
46
config/attributes/item.yml
Normal file
@ -0,0 +1,46 @@
|
||||
:overview:
|
||||
:label: overview
|
||||
:icon: info
|
||||
:attributes:
|
||||
- :name: name
|
||||
:label: Name
|
||||
- :name: item_type
|
||||
:label: Item Type
|
||||
- :name: description
|
||||
:label: Description
|
||||
- :name: universe_id
|
||||
:label: Universe
|
||||
:looks:
|
||||
:label: Looks
|
||||
:icon: redeem
|
||||
:attributes:
|
||||
- :name: materials
|
||||
:label: Materials
|
||||
- :name: weight
|
||||
:label: Weight
|
||||
:history:
|
||||
:label: History
|
||||
:icon: book
|
||||
:attributes:
|
||||
- :name: original_owners
|
||||
:label: Original Owners
|
||||
- :name: current_owners
|
||||
:label: Current Owners
|
||||
- :name: makers
|
||||
:label: Makers
|
||||
- :name: year_made
|
||||
:label: Year it was made
|
||||
:abilities:
|
||||
:label: Abilities
|
||||
:icon: flash_on
|
||||
:attributes:
|
||||
- :name: magic
|
||||
:label: Magic
|
||||
:notes:
|
||||
:label: Notes
|
||||
:icon: edit
|
||||
:attributes:
|
||||
- :name: notes
|
||||
:label: Notes
|
||||
- :name: private_notes
|
||||
:label: Private Notes
|
||||
62
config/attributes/location.yml
Normal file
62
config/attributes/location.yml
Normal file
@ -0,0 +1,62 @@
|
||||
:overview:
|
||||
:label: overview
|
||||
:icon: info
|
||||
:attributes:
|
||||
- :name: name
|
||||
:label: Name
|
||||
- :name: type_of
|
||||
:label: Type
|
||||
- :name: description
|
||||
:label: Description
|
||||
- :name: universe_id
|
||||
:label: Universe
|
||||
:culture:
|
||||
:label: Culture
|
||||
:icon: face
|
||||
:attributes:
|
||||
- :name: leaders
|
||||
:label: Leaders
|
||||
- :name: population
|
||||
:label: Population
|
||||
- :name: language
|
||||
:label: Language
|
||||
- :name: currency
|
||||
:label: Currency
|
||||
- :name: motto
|
||||
:label: Motto
|
||||
:cities:
|
||||
:label: Cities
|
||||
:icon: business
|
||||
:attributes:
|
||||
- :name: capital_cities
|
||||
:label: Capital Cities
|
||||
- :name: largest_cities
|
||||
:label: Largest Cities
|
||||
- :name: notable_cities
|
||||
:label: Notable Cities
|
||||
:geography:
|
||||
:label: Geography
|
||||
:icon: map
|
||||
:attributes:
|
||||
- :name: area
|
||||
:label: Area
|
||||
- :name: crops
|
||||
:label: Crops
|
||||
- :name: located_at
|
||||
:label: Located At
|
||||
:history:
|
||||
:label: History
|
||||
:icon: book
|
||||
:attributes:
|
||||
- :name: established_year
|
||||
:label: Established Year
|
||||
- :name: notable_wars
|
||||
:label: Notable Wars
|
||||
:notes:
|
||||
:label: Notes
|
||||
:icon: edit
|
||||
:attributes:
|
||||
- :name: notes
|
||||
:label: Notes
|
||||
- :name: private_notes
|
||||
:label: Private Notes
|
||||
28
config/attributes/universe.yml
Normal file
28
config/attributes/universe.yml
Normal file
@ -0,0 +1,28 @@
|
||||
:overview:
|
||||
:label: overview
|
||||
:icon: info
|
||||
:attributes:
|
||||
- :name: name
|
||||
:label: Name
|
||||
- :name: description
|
||||
:label: Description
|
||||
:history:
|
||||
:label: History
|
||||
:icon: book
|
||||
:attributes:
|
||||
- :name: history
|
||||
:label: History
|
||||
:notes:
|
||||
:label: Notes
|
||||
:icon: edit
|
||||
:attributes:
|
||||
- :name: notes
|
||||
:label: Notes
|
||||
- :name: private_notes
|
||||
:label: Private Notes
|
||||
:settings:
|
||||
:label: Settings
|
||||
:icon: build
|
||||
:attributes:
|
||||
- :name: privacy
|
||||
:label: Privacy
|
||||
@ -143,6 +143,9 @@ en:
|
||||
item: >
|
||||
Books and weapons and artifacts, oh my! Track the items in your world: who made them, who owns them,
|
||||
and what they can do.
|
||||
attributefield: >
|
||||
Attributes can be anything you need to define just the detail you need. Select the type of content
|
||||
you're describing and setup any number of attributes to do the job.
|
||||
|
||||
create_success: "%{model_name} was successfully created."
|
||||
update_success: "%{model_name} was successfully updated."
|
||||
|
||||
@ -25,6 +25,10 @@ Rails.application.routes.draw do
|
||||
|
||||
# Planning
|
||||
scope '/plan' do
|
||||
resources :attributes
|
||||
resources :attribute_categories
|
||||
resources :attribute_fields
|
||||
|
||||
# Characters
|
||||
resources :characters do
|
||||
get :autocomplete_character_name, on: :collection, as: :autocomplete_name
|
||||
@ -33,6 +37,7 @@ Rails.application.routes.draw do
|
||||
resources :locations do
|
||||
get :autocomplete_location_name, on: :collection, as: :autocomplete_name
|
||||
end
|
||||
|
||||
resources :universes
|
||||
|
||||
# Coming Soon TM
|
||||
@ -45,6 +50,7 @@ Rails.application.routes.draw do
|
||||
|
||||
scope 'admin' do
|
||||
get '/', to: 'admin#dashboard', as: :admin_dashboard
|
||||
get '/attributes', to: 'admin#attributes', as: :admin_attributes
|
||||
get '/universes', to: 'admin#universes', as: :admin_universes
|
||||
get '/characters', to: 'admin#characters', as: :admin_characters
|
||||
get '/locations', to: 'admin#locations', as: :admin_locations
|
||||
|
||||
45
db/migrate/20161003183741_create_attributes.rb
Normal file
45
db/migrate/20161003183741_create_attributes.rb
Normal file
@ -0,0 +1,45 @@
|
||||
class CreateAttributes < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :attribute_categories do |t|
|
||||
t.belongs_to :user
|
||||
t.string :entity_type
|
||||
t.string :name, null: false
|
||||
t.string :label, null: false
|
||||
t.string :icon
|
||||
t.text :description
|
||||
t.timestamps
|
||||
end
|
||||
add_index :attribute_categories, :entity_type
|
||||
add_index :attribute_categories, :name
|
||||
|
||||
create_table :attribute_fields do |t|
|
||||
t.belongs_to :user
|
||||
t.integer :attribute_category_id, null: false
|
||||
|
||||
t.string :name, null: false
|
||||
t.string :label, null: false
|
||||
t.string :field_type, null: false
|
||||
t.text :description
|
||||
t.string :privacy, default: 'private', null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
add_index :attribute_fields, [:user_id, :name]
|
||||
|
||||
create_table :attributes do |t|
|
||||
t.belongs_to :user
|
||||
t.integer :attribute_field_id
|
||||
|
||||
# Polymorphic association to owning entity
|
||||
t.string :entity_type, null: false
|
||||
t.integer :entity_id, null: false
|
||||
|
||||
# Attribute values
|
||||
t.text :value
|
||||
t.string :privacy, default: 'private', null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
add_index :attributes, [:user_id, :entity_type, :entity_id]
|
||||
end
|
||||
end
|
||||
45
db/schema.rb
45
db/schema.rb
@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20160922204317) do
|
||||
ActiveRecord::Schema.define(version: 20161003183741) do
|
||||
|
||||
create_table "archenemyships", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
@ -21,6 +21,49 @@ ActiveRecord::Schema.define(version: 20160922204317) do
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "attribute_categories", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.string "entity_type"
|
||||
t.string "name", null: false
|
||||
t.string "label", null: false
|
||||
t.string "icon"
|
||||
t.text "description"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "attribute_categories", ["entity_type"], name: "index_attribute_categories_on_entity_type"
|
||||
add_index "attribute_categories", ["name"], name: "index_attribute_categories_on_name"
|
||||
|
||||
create_table "attribute_fields", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.integer "universe_id"
|
||||
t.integer "attribute_category_id", null: false
|
||||
t.string "name", null: false
|
||||
t.string "label", null: false
|
||||
t.string "field_type", null: false
|
||||
t.text "description"
|
||||
t.string "privacy", default: "private", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "attribute_fields", ["user_id", "name"], name: "index_attribute_fields_on_user_id_and_name"
|
||||
|
||||
create_table "attributes", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.integer "universe_id"
|
||||
t.integer "attribute_field_id"
|
||||
t.string "entity_type", null: false
|
||||
t.integer "entity_id", null: false
|
||||
t.text "value"
|
||||
t.string "privacy", default: "private", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
add_index "attributes", ["user_id", "entity_type", "entity_id"], name: "index_attributes_on_user_id_and_entity_type_and_entity_id"
|
||||
|
||||
create_table "best_friendships", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.integer "character_id"
|
||||
|
||||
18
db/seeds.rb
18
db/seeds.rb
@ -24,3 +24,21 @@ Item.create(name: 'Sting',
|
||||
Location.create(name: 'The Shire',
|
||||
user: tolkien,
|
||||
universe: middleearth)
|
||||
|
||||
affiliation = AttributeCategory.create(name: 'affiliation',
|
||||
entity_type: 'character',
|
||||
user: tolkien,
|
||||
label: 'Affiliation',
|
||||
icon: 'verified_user')
|
||||
|
||||
affiliation.attribute_fields.create(name: 'starting_affiliation',
|
||||
user: tolkien,
|
||||
universe: middleearth,
|
||||
label: 'Starting Affiliation',
|
||||
field_type: 'text')
|
||||
|
||||
affiliation.attribute_fields.create(name: 'ending_affiliation',
|
||||
user: tolkien,
|
||||
universe: middleearth,
|
||||
label: 'Ending Affiliation',
|
||||
field_type: 'text')
|
||||
|
||||
9
spec/models/attribute_spec.rb
Normal file
9
spec/models/attribute_spec.rb
Normal file
@ -0,0 +1,9 @@
|
||||
require 'rails_helper'
|
||||
require 'support/privacy_example'
|
||||
require 'support/public_scope_example'
|
||||
|
||||
RSpec.describe Attribute, type: :model do
|
||||
it_behaves_like 'content with privacy'
|
||||
it_behaves_like 'content with an is_public scope'
|
||||
it { is_expected.to validate_presence_of(:name) }
|
||||
end
|
||||
Loading…
Reference in New Issue
Block a user