common-content 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.textile +55 -0
  3. data/Rakefile +29 -0
  4. data/app/assets/javascripts/application.js +15 -0
  5. data/app/assets/javascripts/bootstrap.js.coffee +0 -0
  6. data/app/assets/javascripts/contents.js.coffee +16 -0
  7. data/app/assets/javascripts/jquery-ui-1.8.21.custom.min.js +125 -0
  8. data/app/assets/stylesheets/application.css +7 -0
  9. data/app/assets/stylesheets/bootstrap_and_overrides.css.less +29 -0
  10. data/app/assets/stylesheets/contents.css.less +11 -0
  11. data/app/assets/stylesheets/scaffolds.css.less +62 -0
  12. data/app/controllers/contents_controller.rb +99 -0
  13. data/app/helpers/contents_helper.rb +29 -0
  14. data/app/models/content.rb +49 -0
  15. data/app/models/resource.rb +10 -0
  16. data/app/models/seo.rb +11 -0
  17. data/app/models/textile.rb +16 -0
  18. data/app/views/contents/_array_form.html.erb +14 -0
  19. data/app/views/contents/_child_content_fields.html.erb +14 -0
  20. data/app/views/contents/_content.xml.builder +4 -0
  21. data/app/views/contents/_fields_edit.html.erb +7 -0
  22. data/app/views/contents/_fields_new.html.erb +14 -0
  23. data/app/views/contents/_resource_fields.html.erb +21 -0
  24. data/app/views/contents/_seo_fields.html.erb +21 -0
  25. data/app/views/contents/_show_children.html.erb +10 -0
  26. data/app/views/contents/_textile_form.html.erb +1 -0
  27. data/app/views/contents/edit.html.erb +44 -0
  28. data/app/views/contents/index.html.erb +34 -0
  29. data/app/views/contents/index.xml.builder +5 -0
  30. data/app/views/contents/new.html.erb +13 -0
  31. data/app/views/contents/show.html.erb +25 -0
  32. data/app/views/contents/show.xml.builder +1 -0
  33. data/app/views/layouts/application.html.erb +64 -0
  34. data/config/routes.rb +9 -0
  35. data/lib/common-content.rb +4 -0
  36. data/lib/common-content/engine.rb +7 -0
  37. data/lib/common-content/version.rb +3 -0
  38. data/lib/tasks/common-content_tasks.rake +4 -0
  39. metadata +180 -0
@@ -0,0 +1,29 @@
1
+ module ContentsHelper
2
+ def hateoas_uri_options(source, entity)
3
+ # open the source url
4
+ # parse out the entities
5
+ # look for <name> and <link rel='self'> and parse into [[link, name]]
6
+ end
7
+
8
+
9
+ # from http://railscasts.com/episodes/196-nested-model-form-revised?view=asciicast
10
+ # def link_to_add_fields(name, f, association)
11
+ # new_object = f.object.send(association).klass.new
12
+ # id = new_object.object_id
13
+ # fields = f.fields_for(association, new_object, child_index: id) do |builder|
14
+ # render(association.to_s.singularize + "_fields", f: builder)
15
+ # end
16
+ # link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
17
+ # end
18
+
19
+ def link_to_add_fields(name, f, association)
20
+ new_object = f.object.send(association).klass.new
21
+ id = new_object.object_id
22
+ fields = f.fields_for(association, new_object, child_index: id) do |builder|
23
+ render(partial: association.to_s.singularize + "_fields", locals: { f: builder })
24
+ end
25
+ link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
26
+ end
27
+
28
+
29
+ end
@@ -0,0 +1,49 @@
1
+ class Content
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+
5
+ field :slug, type: String
6
+ validates :slug, presence: true, uniqueness: true
7
+ key :slug
8
+
9
+ field :title, type: String
10
+ validates :title, presence: true
11
+
12
+ field :body, type: Textile # optional html block of text for this content
13
+
14
+ embeds_one :seo # optional SEO fields
15
+ accepts_nested_attributes_for :seo # mongoid BUG see http://stackoverflow.com/questions/9392315/mongoid-and-nested-form-for-embeds-one-document
16
+
17
+ embeds_many :resources # optional 1-n resources like images, videos, REST urls, etc
18
+ accepts_nested_attributes_for :resources # mongoid BUG see http://stackoverflow.com/questions/9392315/mongoid-and-nested-form-for-embeds-one-document
19
+
20
+ recursively_embeds_many # optionaal 1-n embedded child Content objects, the editable field controls if user can change
21
+ accepts_nested_attributes_for :child_contents # mongoid BUG see http://stackoverflow.com/questions/9392315/mongoid-and-nested-form-for-embeds-one-document
22
+
23
+ # embeds_one :constraint # the constraints of what fields are required, what child objects can be defined and how many, etc.
24
+
25
+ def ancestry
26
+ parent_content ? parent_content.ancestry << id : [id]
27
+ end
28
+
29
+ def self.find_by_ancestry(ancestry = nil, id)
30
+ return self.find(id) unless ancestry
31
+ parent = Content.find(ancestry.shift)
32
+ while(parent_slug = ancestry.shift and parent_slug != id) do
33
+ parent = parent.child_contents.find(parent_slug)
34
+ end if parent
35
+ parent.child_contents.find(id)
36
+ end
37
+
38
+ def child_order=(order)
39
+ return unless order
40
+ order = order.split(',')
41
+ order.each {|e| e.strip!}
42
+ self.child_contents = self.child_contents.sort_by {|kid| order.find_index(kid.id) }
43
+ end
44
+
45
+ def to_param
46
+ parent_content ? parent_content.to_param + '/' + id : id
47
+ end
48
+
49
+ end
@@ -0,0 +1,10 @@
1
+ class Resource
2
+ include Mongoid::Document
3
+
4
+ embedded_in :content
5
+
6
+ field :slug, type: String
7
+ field :uri, type: String # the URI of the entity we're linking to
8
+ field :content_type, type: String # mime type if there is one
9
+
10
+ end
data/app/models/seo.rb ADDED
@@ -0,0 +1,11 @@
1
+ class Seo
2
+ include Mongoid::Document
3
+
4
+ embedded_in :content
5
+
6
+ field :description, type: String
7
+ field :keywords, type: String
8
+ field :metatitle, type: String
9
+
10
+ # validates :description, :keywords, :metatitle, presence: true
11
+ end
@@ -0,0 +1,16 @@
1
+ require 'RedCloth'
2
+
3
+ class Textile < RedCloth::TextileDoc
4
+ include Mongoid::Fields::Serializable
5
+
6
+ attr_accessor :max
7
+
8
+ def serialize(object)
9
+ object.to_s
10
+ end
11
+ def deserialize(object)
12
+ object = '' unless object
13
+ RedCloth.new(object)
14
+ end
15
+
16
+ end
@@ -0,0 +1,14 @@
1
+ <% f.object.send(fk).each_with_index do |ar, idx| %>
2
+ <div class="control-group">
3
+ <%= f.fields_for fk do |ar_form| %>
4
+ <div class="controls">
5
+ <!-- do some stuff to see if this type has a specific view partial -->
6
+ <% if File.file?(File.expand_path("_#{ar.type.to_s.downcase}_form.html.erb", File.dirname(__FILE__))) %>
7
+ <%= render :partial => "#{ar.type.to_s.downcase}_form", :locals => {f: f, fk: fk} %>
8
+ <% else %>
9
+ <%= ar_form.text_field "", :class => 'text_field', :value => ar %>
10
+ <% end %>
11
+ </div>
12
+ <% end %>
13
+ </div>
14
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <fieldset>
2
+ <div class="control-group">
3
+ <%= f.label :slug, :class => 'control-label' %>
4
+ <div class="controls">
5
+ <%= f.text_field :slug, :class => 'text_field' %>
6
+ </div>
7
+ </div>
8
+ <div class="control-group">
9
+ <%= f.label :title, :class => 'control-label' %>
10
+ <div class="controls">
11
+ <%= f.text_field :title, :class => 'text_field' %>
12
+ </div>
13
+ </div>
14
+ </fieldset>
@@ -0,0 +1,4 @@
1
+ xml.content do
2
+ xml.name(content.slug)
3
+ xml.link('rel' => 'self', 'uri' => content_path(content))
4
+ end
@@ -0,0 +1,7 @@
1
+ <div class="control-group">
2
+ <%= f.label :body, :class => 'control-label' %>
3
+ <div class="controls">
4
+ <%= f.text_area :body, :class => 'text_area' %>
5
+ </div>
6
+ </div>
7
+
@@ -0,0 +1,14 @@
1
+ <fieldset>
2
+ <div class="control-group">
3
+ <%= f.label :slug, :class => 'control-label' %>
4
+ <div class="controls">
5
+ <%= f.text_field :slug, :class => 'text_field', disabled: !@content.new? %>
6
+ </div>
7
+ </div>
8
+ <div class="control-group">
9
+ <%= f.label 'title', :class => 'control-label' %>
10
+ <div class="controls">
11
+ <%= f.text_field :title, :class => 'text_field' %>
12
+ </div>
13
+ </div>
14
+ </fieldset>
@@ -0,0 +1,21 @@
1
+ <fieldset>
2
+ <div class="control-group">
3
+ <%= f.label :slug, :class => 'control-label' %>
4
+ <div class="controls">
5
+ <%= f.text_field :slug, :class => 'text_field' %>
6
+ </div>
7
+ </div>
8
+ <div class="control-group">
9
+ <%= f.label :uri, :class => 'control-label' %>
10
+ <div class="controls">
11
+ <%= f.text_field :uri, :class => 'text_field' %>
12
+ </div>
13
+ </div>
14
+ <div class="control-group">
15
+ <%= f.label :type, :class => 'control-label' %>
16
+ <div class="controls">
17
+ <%= f.text_field :content_type, :class => 'text_field' %>
18
+ </div>
19
+ </div>
20
+ <hr/>
21
+ </fieldset>
@@ -0,0 +1,21 @@
1
+ <fieldset>
2
+ <div class="control-group">
3
+ <%= f.label :title, :class => 'control-label' %>
4
+ <div class="controls">
5
+ <%= f.text_field :metatitle, :class => 'text_field' %>
6
+ </div>
7
+ </div>
8
+ <div class="control-group">
9
+ <%= f.label :description, :class => 'control-label' %>
10
+ <div class="controls">
11
+ <%= f.text_area :description, :class => 'text_area meta_description', rows: '3' %>
12
+ </div>
13
+ </div>
14
+ <div class="control-group">
15
+ <%= f.label :keywords, :class => 'control-label' %>
16
+ <div class="controls">
17
+ <%= f.text_field :keywords, :class => 'text_field' %>
18
+ </div>
19
+ </div>
20
+ <hr/>
21
+ </fieldset>
@@ -0,0 +1,10 @@
1
+ <% # http://stackoverflow.com/questions/187073/jquery-sortables %>
2
+ <div class="">
3
+ <!-- <input type='hidden' name='content[child_order]' id='child_order' /> -->
4
+ <div id="children">
5
+ <% show_children.try :each do |child| %>
6
+ <%= content_tag :div, id: child.id do %>
7
+ <p><%= link_to child.title, contents_path + '/' + child.ancestry.join('/') + '/edit' %></p>
8
+ <% end; end -%>
9
+ </div>
10
+ </div>
@@ -0,0 +1 @@
1
+ <%= f.text_area fk.intern, :class => 'text_area' %>
@@ -0,0 +1,44 @@
1
+ <%- model_class = @content.class -%>
2
+ <div class="page-header">
3
+ <h3><%=t '.title', :default => t('helpers.titles.define', :model => model_class.model_name.human,
4
+ :default => "Edit #{@content.slug or model_class.model_name.human}") %></h3>
5
+ </div>
6
+ <%= form_for @content, :html => { :class => 'form-horizontal' } do |f| %>
7
+ <% if @content.errors.any? %>
8
+ <div class="errors">
9
+ <h3>ERROR!</h3>
10
+ <ul>
11
+ <% @content.errors.full_messages.each do |msg| %>
12
+ <%= content_tag :li, msg %>
13
+ <% end %>
14
+ </ul>
15
+ </div>
16
+ <% end %>
17
+ <%= render :partial => 'fields_new', :locals => {f: f} %>
18
+ <%= render :partial => 'fields_edit', :locals => {f: f} %>
19
+ <hr/>
20
+ <h4>Seo</h4>
21
+ <%= f.fields_for :seo do |seo| %>
22
+ <%= render :partial => 'seo_fields', :locals => {f: seo} %>
23
+ <% end %>
24
+ <h4>Resources</h4>
25
+ <%= link_to_add_fields "Add Resource", f, :resources %>
26
+
27
+ <%= f.fields_for :resources do |res| %>
28
+ <%= render :partial => 'resource_fields', :locals => {f: res} %>
29
+ <% end %>
30
+ <h4>Children</h4>
31
+ <%= link_to_add_fields "Add Child", f, :child_contents %>
32
+ <%= render :partial => 'show_children', object: @content.child_contents %>
33
+ <div class="form-actions">
34
+ <%= f.submit nil, :class => 'btn btn-primary' %>
35
+ <%= link_to t('.cancel', :default => t("helpers.links.cancel")),
36
+ contents_path, :class => 'btn' %>
37
+ </div>
38
+
39
+ <% end %>
40
+
41
+ <% content_for :breadcrumb do %>
42
+ <%= content_tag :anchor, @content.ancestry %>
43
+ <% end %>
44
+
@@ -0,0 +1,34 @@
1
+ <%- model_class = Content.new.class -%>
2
+ <div class="page-header">
3
+ <h1><%=t '.title', :default => model_class.model_name.human.pluralize %></h1>
4
+ </div>
5
+ <table class="table table-striped">
6
+ <thead>
7
+ <tr>
8
+ <th><%= model_class.human_attribute_name(:slug) %></th>
9
+ <th><%= model_class.human_attribute_name(:title) %></th>
10
+ <th><%=t '.actions', :default => t("helpers.actions") %></th>
11
+ </tr>
12
+ </thead>
13
+ <tbody>
14
+ <% @contents.each do |content| %>
15
+ <tr>
16
+ <td><%= link_to content.slug, content_path(content) %></td>
17
+ <td><%= content.title %></td>
18
+ <td>
19
+ <%= link_to t('.edit', :default => t("helpers.links.edit")),
20
+ edit_content_path(content), :class => 'btn btn-mini' %>
21
+ <%= link_to t('.destroy', :default => t("helpers.links.destroy")),
22
+ content_path(content),
23
+ :method => :delete,
24
+ :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')),
25
+ :class => 'btn btn-mini btn-danger' %>
26
+ </td>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
30
+ </table>
31
+
32
+ <%= link_to t('.new', :default => t("helpers.links.new")),
33
+ new_content_path,
34
+ :class => 'btn btn-primary' %>
@@ -0,0 +1,5 @@
1
+ xml.contents do
2
+ @contents.each do |c|
3
+ xml << render(:partial => 'content.xml.builder', object: c)
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ <%- model_class = @content.class -%>
2
+ <div class="page-header">
3
+ <h1><%=t '.title', :default => t('helpers.titles.new', :model => model_class.model_name.human,
4
+ :default => "New #{model_class.model_name.human}") %></h1>
5
+ </div>
6
+ <%= form_for @content, :url => contents_path, :as => :content, :html => { :class => 'form-horizontal' } do |f| %>
7
+ <%= render :partial => 'fields_new', :locals => {f: f} %>
8
+ <div class="form-actions">
9
+ <%= f.submit nil, :class => 'btn btn-primary' %>
10
+ <%= link_to t('.cancel', :default => t("helpers.links.cancel")),
11
+ contents_path, :class => 'btn' %>
12
+ </div>
13
+ <% end %>
@@ -0,0 +1,25 @@
1
+ <%- model_class = @content.class -%>
2
+ <div class="page-header">
3
+ <h1><%=t '.title', :default => "#{@content.title}" %></h1>
4
+ </div>
5
+
6
+ <dl class="dl-horizontal">
7
+ <dt><strong><%= model_class.human_attribute_name(:_type) %>:</strong></dt>
8
+ <dd><%= @content._type %></dd>
9
+ <dt><strong><%= model_class.human_attribute_name(:_id) %>:</strong></dt>
10
+ <dd><%= @content._id %></dd>
11
+ <dt><strong><%= model_class.human_attribute_name(:slug) %>:</strong></dt>
12
+ <dd><%= @content.slug %></dd>
13
+ </dl>
14
+
15
+ <div class="form-actions">
16
+ <%= link_to t('.back', :default => t("helpers.links.back")),
17
+ contents_path, :class => 'btn' %>
18
+ <%= link_to t('.edit', :default => t("helpers.links.edit")),
19
+ edit_content_path(@content.ancestry), :class => 'btn' %>
20
+ <%= link_to t('.destroy', :default => t("helpers.links.destroy")),
21
+ content_path(@content),
22
+ :method => 'delete',
23
+ :confirm => t('.confirm', :default => t("helpers.links.confirm", :default => 'Are you sure?')),
24
+ :class => 'btn btn-danger' %>
25
+ </div>
@@ -0,0 +1 @@
1
+ xml << render(partial:'content.xml.builder')
@@ -0,0 +1,64 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title><%= content_for?(:title) ? yield(:title) : "Common Content" %></title>
7
+ <%= csrf_meta_tags %>
8
+
9
+ <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
10
+ <!--[if lt IE 9]>
11
+ <script src="http://html5shim.googlecode.com/svn/trunk/html5.js" type="text/javascript"></script>
12
+ <![endif]-->
13
+
14
+ <%= stylesheet_link_tag "application", :media => "all" %>
15
+
16
+ <link href="images/favicon.ico" rel="shortcut icon">
17
+ <link href="images/apple-touch-icon.png" rel="apple-touch-icon">
18
+ <link href="images/apple-touch-icon-72x72.png" rel="apple-touch-icon" sizes="72x72">
19
+ <link href="images/apple-touch-icon-114x114.png" rel="apple-touch-icon" sizes="114x114">
20
+ </head>
21
+ <body>
22
+
23
+ <div class="navbar navbar-fixed-top">
24
+ <div class="navbar-inner">
25
+ <div class="container-fluid">
26
+ <a class="btn btn-navbar" data-target=".nav-collapse" data-toggle="collapse">
27
+ <span class="icon-bar"></span>
28
+ <span class="icon-bar"></span>
29
+ <span class="icon-bar"></span>
30
+ </a>
31
+ <a class="brand" href="#">Common Content</a>
32
+ <div class="container-fluid nav-collapse">
33
+ <ul class="nav">
34
+ <li><%= link_to "Contents", "/contents" %></li>
35
+ <li><%= link_to "Admin", "/admin" %></li>
36
+ <li><%= content_for?(:breadcrumb) ? yield(:breadcrumb) : '' %></li>
37
+ </ul>
38
+ </div><!--/.nav-collapse -->
39
+ </div>
40
+ </div>
41
+ </div>
42
+
43
+ <div class="container-fluid">
44
+ <div class="row-fluid">
45
+ <div class="span12">
46
+ <%= yield %>
47
+ </div>
48
+ </div><!--/row-->
49
+
50
+ <footer>
51
+ <p>&copy; 2012</p>
52
+ <h6 style="font-size:8px">view_paths = <%= controller.view_paths.entries %></h6>
53
+ <h6 style="font-size:8px">ancestry = <%= @content.try(:ancestry) %></h6>
54
+ </footer>
55
+
56
+ </div> <!-- /container -->
57
+
58
+ <!-- Javascripts
59
+ ================================================== -->
60
+ <!-- Placed at the end of the document so the pages load faster -->
61
+ <%= javascript_include_tag "application" %>
62
+
63
+ </body>
64
+ </html>