tandem 0.2.0.rc

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +205 -0
  3. data/Rakefile +31 -0
  4. data/app/assets/images/tandem/blank_image.jpg +0 -0
  5. data/app/assets/images/tandem/colorbox/border1.png +0 -0
  6. data/app/assets/images/tandem/colorbox/border2.png +0 -0
  7. data/app/assets/images/tandem/colorbox/ie6/borderBottomCenter.png +0 -0
  8. data/app/assets/images/tandem/colorbox/ie6/borderBottomLeft.png +0 -0
  9. data/app/assets/images/tandem/colorbox/ie6/borderBottomRight.png +0 -0
  10. data/app/assets/images/tandem/colorbox/ie6/borderMiddleLeft.png +0 -0
  11. data/app/assets/images/tandem/colorbox/ie6/borderMiddleRight.png +0 -0
  12. data/app/assets/images/tandem/colorbox/ie6/borderTopCenter.png +0 -0
  13. data/app/assets/images/tandem/colorbox/ie6/borderTopLeft.png +0 -0
  14. data/app/assets/images/tandem/colorbox/ie6/borderTopRight.png +0 -0
  15. data/app/assets/images/tandem/colorbox/loading.gif +0 -0
  16. data/app/assets/images/tandem/ic-delete_image.png +0 -0
  17. data/app/assets/images/tandem/ic-star.png +0 -0
  18. data/app/assets/images/tandem/loader.gif +0 -0
  19. data/app/assets/images/tandem/loader_stripe.png +0 -0
  20. data/app/assets/images/tandem/tandem-editImage2.jpg +0 -0
  21. data/app/assets/images/tandem/tandem_logo.png +0 -0
  22. data/app/assets/images/tandem/tandem_logo_nav.png +0 -0
  23. data/app/assets/javascripts/tandem/contents.js +2 -0
  24. data/app/assets/javascripts/tandem/current_image.jst.eco +19 -0
  25. data/app/assets/javascripts/tandem/gallery_image.jst.eco +10 -0
  26. data/app/assets/javascripts/tandem/images.coffee +42 -0
  27. data/app/assets/javascripts/tandem/pages.js.coffee +32 -0
  28. data/app/assets/javascripts/tandem/popup.js +23 -0
  29. data/app/assets/javascripts/tandem.js +16 -0
  30. data/app/assets/stylesheets/tandem/colorbox.css.erb +83 -0
  31. data/app/assets/stylesheets/tandem/contents.scss +541 -0
  32. data/app/assets/stylesheets/tandem/scaffold.scss +61 -0
  33. data/app/assets/stylesheets/tandem/variables.scss +38 -0
  34. data/app/assets/stylesheets/tandem.css +7 -0
  35. data/app/controllers/tandem/application_controller.rb +19 -0
  36. data/app/controllers/tandem/contents_controller.rb +94 -0
  37. data/app/controllers/tandem/images_controller.rb +92 -0
  38. data/app/controllers/tandem/pages_controller.rb +108 -0
  39. data/app/helpers/tandem/application_helper.rb +29 -0
  40. data/app/helpers/tandem/contents_helper.rb +19 -0
  41. data/app/helpers/tandem/images_helper.rb +10 -0
  42. data/app/helpers/tandem/pages_helper.rb +243 -0
  43. data/app/models/tandem/ability.rb +6 -0
  44. data/app/models/tandem/content/image.rb +7 -0
  45. data/app/models/tandem/content/text.rb +7 -0
  46. data/app/models/tandem/content.rb +41 -0
  47. data/app/models/tandem/image.rb +27 -0
  48. data/app/models/tandem/page.rb +41 -0
  49. data/app/views/layouts/tandem/image.html.slim +19 -0
  50. data/app/views/layouts/tandem/popup.html.slim +17 -0
  51. data/app/views/tandem/contents/_form.html.slim +36 -0
  52. data/app/views/tandem/contents/edit.html.slim +3 -0
  53. data/app/views/tandem/contents/index.html.slim +29 -0
  54. data/app/views/tandem/contents/new.html.slim +3 -0
  55. data/app/views/tandem/contents/show.html.slim +20 -0
  56. data/app/views/tandem/images/_form.html.slim +24 -0
  57. data/app/views/tandem/images/_gallery.html.slim +4 -0
  58. data/app/views/tandem/images/edit.html.slim +8 -0
  59. data/app/views/tandem/images/index.html.slim +9 -0
  60. data/app/views/tandem/images/new.html.slim +4 -0
  61. data/app/views/tandem/images/show.html.slim +10 -0
  62. data/app/views/tandem/images/thumb.html.slim +1 -0
  63. data/app/views/tandem/pages/_form.html.slim +56 -0
  64. data/app/views/tandem/pages/edit.html.slim +1 -0
  65. data/app/views/tandem/pages/index.html.slim +35 -0
  66. data/app/views/tandem/pages/new.html.slim +1 -0
  67. data/app/views/tandem/pages/show.html.slim +16 -0
  68. data/config/cucumber.yml +8 -0
  69. data/config/routes.rb +17 -0
  70. data/db/migrate/20111122221549_create_tandem_pages.rb +20 -0
  71. data/db/migrate/20111122222037_create_tandem_contents.rb +21 -0
  72. data/db/migrate/20111215001943_create_tandem_images.rb +12 -0
  73. data/db/migrate/30000000000000_create_default_page.rb +10 -0
  74. data/db/migrate/30000000000001_add_request_key_to_tandem_contents.rb +44 -0
  75. data/lib/generators/tandem_generator.rb +26 -0
  76. data/lib/generators/templates/initializer.rb +70 -0
  77. data/lib/tandem/engine.rb +23 -0
  78. data/lib/tandem/version.rb +3 -0
  79. data/lib/tandem.rb +46 -0
  80. data/lib/tasks/cucumber.rake +65 -0
  81. data/lib/tasks/tandem_tasks.rake +4 -0
  82. metadata +354 -0
@@ -0,0 +1,94 @@
1
+ module Tandem
2
+ class ContentsController < ::Tandem::ApplicationController
3
+ load_and_authorize_resource
4
+ layout 'tandem/popup'
5
+
6
+ =begin ### default scaffold actions not currently used
7
+ # GET /contents
8
+ # GET /contents.json
9
+ def index
10
+ @contents = @page.contents.all
11
+ authorize_content!
12
+
13
+ respond_to do |format|
14
+ format.html # index.html.erb
15
+ format.json { render json: @contents }
16
+ end
17
+ end
18
+
19
+ # GET /contents/1
20
+ # GET /contents/1.json
21
+ def show
22
+ @content = @page.contents.find(params[:id])
23
+ authorize_content!
24
+
25
+ respond_to do |format|
26
+ format.html # show.html.erb
27
+ format.json { render json: @content }
28
+ end
29
+ end
30
+
31
+ # GET /contents/new
32
+ # GET /contents/new.json
33
+ def new
34
+ @content = @page.contents.new
35
+ authorize_content!
36
+
37
+ respond_to do |format|
38
+ format.html # new.html.erb
39
+ format.json { render json: @content }
40
+ end
41
+ end
42
+
43
+ # POST /contents
44
+ # POST /contents.json
45
+ def create
46
+ @content = Content::Text.new(params[:content].merge(:page_id => @page.id))
47
+ authorize_content!
48
+
49
+ respond_to do |format|
50
+ if @content.save
51
+ format.html { render action: "success", notice: 'Content was successfully created.' }
52
+ format.json { render json: @content, status: :created, location: @content }
53
+ else
54
+ format.html { render action: "new" }
55
+ format.json { render json: @content.errors, status: :unprocessable_entity }
56
+ end
57
+ end
58
+ end
59
+
60
+ # DELETE /contents/1
61
+ # DELETE /contents/1.json
62
+ def destroy
63
+ @content = @page.contents.find(params[:id])
64
+ authorize_content!
65
+
66
+ @content.destroy
67
+
68
+ respond_to do |format|
69
+ format.html { redirect_to page_contents_url }
70
+ format.json { head :ok }
71
+ end
72
+ end
73
+
74
+ =end
75
+
76
+ # GET /contents/1/edit
77
+ def edit
78
+ end
79
+
80
+ # PUT /contents/1
81
+ # PUT /contents/1.json
82
+ def update
83
+ param_key = ActiveModel::Naming.param_key(@content)
84
+
85
+ respond_to do |format|
86
+ if @content.update_attributes(params[param_key])
87
+ format.json { render json: @content }
88
+ else
89
+ format.json { render json: @content.errors, status: :unprocessable_entity }
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,92 @@
1
+ module Tandem
2
+ class ImagesController < ::Tandem::ApplicationController
3
+ load_and_authorize_resource
4
+ skip_load_and_authorize_resource :only => :thumb
5
+ skip_authorization_check :only => :thumb
6
+ layout 'tandem/image'
7
+
8
+ # GET /images
9
+ # GET /images.json
10
+ def index
11
+ respond_to do |format|
12
+ format.html # index.html.erb
13
+ format.json { render json: @images }
14
+ end
15
+ end
16
+
17
+ # GET /images/1
18
+ # GET /images/1.json
19
+ def show
20
+ respond_to do |format|
21
+ format.html #show.html.erb
22
+ format.json { render json: @image }
23
+ end
24
+ end
25
+
26
+ # GET /images/1/thumb
27
+ # GET /images/1/thumb.json
28
+ #this method is deliberately forgiving to image id's that don't exist
29
+ def thumb
30
+ @image = Tandem::Image.find_by_id(params[:id])
31
+ @content = Tandem::Content::Image.new(image: @image)
32
+ respond_to do |format|
33
+ format.html { render layout: nil }# thumb.html.erb
34
+ end
35
+ end
36
+
37
+ # GET /images/new
38
+ # GET /images/new.json
39
+ def new
40
+ @update_gallery = params['update_gallery'].present?
41
+ respond_to do |format|
42
+ format.html # new.html.erb
43
+ format.json { render json: @image }
44
+ end
45
+ end
46
+
47
+ # GET /images/1/edit
48
+ def edit
49
+ end
50
+
51
+ # POST /images
52
+ # POST /images.json
53
+ def create
54
+ respond_to do |format|
55
+ if @image.save
56
+ format.html { redirect_to new_image_path(update_gallery: true), notice: 'Image was successfully created.' }
57
+ format.json { render json: @image, status: :created, location: @image }
58
+ else
59
+ format.html { render action: "new" }
60
+ format.json { render json: @image.errors, status: :unprocessable_entity }
61
+ end
62
+ end
63
+ end
64
+
65
+ # PUT /images/1
66
+ # PUT /images/1.json
67
+ def update
68
+ respond_to do |format|
69
+ if @image.update_attributes(params[:image])
70
+ format.html { redirect_to @image, notice: 'Image was successfully updated.' }
71
+ format.json { head :ok }
72
+ else
73
+ format.html { render action: "edit" }
74
+ format.json { render json: @image.errors, status: :unprocessable_entity }
75
+ end
76
+ end
77
+ end
78
+
79
+ # DELETE /images/1
80
+ # DELETE /images/1.json
81
+ def destroy
82
+ @image.destroy
83
+
84
+ respond_to do |format|
85
+ format.html { redirect_to images_url }
86
+ format.json { head :ok }
87
+ end
88
+ end
89
+
90
+ private
91
+ end
92
+ end
@@ -0,0 +1,108 @@
1
+ module Tandem
2
+ class PagesController < ::Tandem::ApplicationController
3
+ load_and_authorize_resource :except => :home, :find_by => :slug
4
+ layout :pages_layout
5
+
6
+ # GET /pages/home
7
+ # GET /pages.home.json
8
+ def home
9
+ @page = Page.where(is_default: true).first || Page.first || create_default_page
10
+ authorize!(:show, Page)
11
+ respond_to do |format|
12
+ format.html { render (@page.template.present? ? @page.template : 'show'), notice: @page.new_record? ? 'No Pages Found.' : '' }
13
+ format.json { render json: @page }
14
+ end
15
+ end
16
+
17
+ # GET /pages
18
+ # GET /pages.json
19
+ def index
20
+ respond_to do |format|
21
+ format.html # index.html.erb
22
+ format.json { render json: @pages }
23
+ end
24
+ end
25
+
26
+ # GET /pages/1
27
+ # GET /pages/1.json
28
+ def show
29
+ respond_to do |format|
30
+ format.html { render @page.template if @page.template.present? }
31
+ format.json { render json: @page }
32
+ end
33
+ end
34
+
35
+ # GET /pages/new
36
+ # GET /pages/new.json
37
+ def new
38
+ @page.parent ||= Page.where(id: params['parent_id']).first
39
+ respond_to do |format|
40
+ format.html { render layout: false if request.xhr? }
41
+ format.json { render json: @page }
42
+ end
43
+ end
44
+
45
+ # GET /pages/1/edit
46
+ def edit
47
+ respond_to do |format|
48
+ format.html { render layout: false if request.xhr? }
49
+ end
50
+ end
51
+
52
+ # POST /pages
53
+ # POST /pages.json
54
+ def create
55
+ respond_to do |format|
56
+ if @page.save
57
+ format.html { redirect_to @page, notice: 'Page was successfully created.' }
58
+ format.json { render json: @page, status: :created, location: @page }
59
+ else
60
+ format.html { render action: "new" }
61
+ format.json { render json: @page.errors, status: :unprocessable_entity }
62
+ end
63
+ end
64
+ end
65
+
66
+ # PUT /pages/1
67
+ # PUT /pages/1.json
68
+ def update
69
+ respond_to do |format|
70
+ if @page.update_attributes(params[:page])
71
+ format.html { redirect_to @page, notice: 'Page was successfully updated.' }
72
+ format.json { head :no_content }
73
+ else
74
+ format.html { render action: "edit" }
75
+ format.json { render json: @page.errors, status: :unprocessable_entity }
76
+ end
77
+ end
78
+ end
79
+
80
+ # DELETE /pages/1
81
+ # DELETE /pages/1.json
82
+ def destroy
83
+ @page.destroy
84
+
85
+ respond_to do |format|
86
+ format.html { redirect_to pages_url }
87
+ format.json { head :ok }
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def pages_layout
94
+ case params[:action]
95
+ when 'show', 'home'
96
+ @page.layout.present? ? @page.layout : 'application'
97
+ when 'new', 'create', 'edit', 'update'
98
+ 'tandem/popup'
99
+ else
100
+ 'application'
101
+ end
102
+ end
103
+
104
+ def create_default_page
105
+ Page.create!(link_label: 'Sample Page', slug: 'sample', is_default: true)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,29 @@
1
+ module Tandem
2
+ module ApplicationHelper
3
+ def self.included(base)
4
+ main_app_url_helpers.each do |helper|
5
+ base.send(:define_method, helper) do |*arguments|
6
+ arguments = nil if arguments.empty?
7
+ main_app.send(helper, arguments)
8
+ end
9
+ end
10
+ end
11
+
12
+
13
+ def render_eco_template(template, locals = {})
14
+ @eco_templates ||= {}
15
+
16
+ if @eco_templates[template].nil?
17
+ template_file = File.read("#{Tandem::Engine.config.root}/app/assets/javascripts/tandem/#{template}.jst.eco")
18
+ @eco_templates[template] = Eco.context_for(template_file)
19
+ end
20
+
21
+ @eco_templates[template].call("render", locals).html_safe
22
+ end
23
+
24
+ private
25
+ def self.main_app_url_helpers
26
+ ::Rails.application.routes.url_helpers.methods.select{|method| method.match(/^(?!hash\_for).*_(path|url)$/)}
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ module Tandem
2
+ module ContentsHelper
3
+
4
+ def image_content_url(image_content, format = :original)
5
+ image_content.image.present? ? image_content.image.resource.url(format) : 'tandem/blank_image.jpg'
6
+ end
7
+
8
+ def image_content_tag(image_content, options = {}, format = :original)
9
+ options = {style: ""}.merge(options) if format == :thumb
10
+ image_tag image_content_url(image_content, format), options
11
+ end
12
+
13
+ def text_content_text_area_options(editor = 'plain', options = {})
14
+ options.tap do |options|
15
+ options[:class] = 'wymeditor' if editor == 'wysiwyg'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ module Tandem
2
+ module ImagesHelper
3
+
4
+ def tandem_image_tag(image, options = {}, format = nil)
5
+ options = {style: "height:#{Tandem::Image::THUMB_HEIGHT}px;width:#{Tandem::Image::THUMB_WIDTH}px;border:5px solid #ccc;"}.merge(options) if format == :thumb
6
+ image_tag image.resource.url(format), options
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,243 @@
1
+ module Tandem
2
+ module PagesHelper
3
+
4
+ include ContentsHelper
5
+
6
+ def self.included(base)
7
+ Tandem::Content.subclasses.each do |klass|
8
+ # tandem_#{klass.simple_type}_tag is provided as an alias of tandem_content_tag,
9
+ # specifying the type of content through the method name. For example:
10
+ #
11
+ # <%= tandem_image_tag(:test_image) %>
12
+ #
13
+ # is equivalent to:
14
+ #
15
+ # <%= tandem_content_tag(:test_image, :image) %>
16
+ #
17
+ base.send(:define_method, "tandem_#{klass.simple_type}_tag") do |identifier, options = {}, html_options = {}|
18
+ tandem_content_tag identifier, klass.simple_type, options, html_options
19
+ end
20
+ end
21
+ end
22
+
23
+ # tandem_content_tag creates an HTML DIV element with id and class parameters that
24
+ # relate to the specified Tandem Content object and contains an internal content element.
25
+ # If a Tandem::Content record doesn't exist for the unique combination of:
26
+ # request_key, identifier and type, one will be created automatically. For example:
27
+ #
28
+ # <%= tandem_content_tag(:test_image, :image) %>
29
+ #
30
+ # would find_or_create a Tandem::Content::Image object and produce the following HTML:
31
+ #
32
+ # <div class="tandem_content" id="test_image">...<img id="tandem_content_image_test_image" class="tandem_content_image bar"...</div>
33
+ #
34
+ # If the user is authorized to update content then an edit link will be generated in
35
+ # conjunction with the content. For example:
36
+ #
37
+ # <%= tandem_content_tag(:test_text, :text) %>
38
+ #
39
+ # would produce the following HTML (assuming `can? :update, tandem_content` => true):
40
+ #
41
+ # <div class="tandem_content" id="test_text">
42
+ # <div class="tandem_toolbar" id="tandem_toolbar_test_text">
43
+ # <a href="#" id="tandem_edit_link_test_text" title="Edit test_text" class="tandem_edit_link" editor_options="{&quot;identifier&quot;:&quot;test_text&quot;,&quot;type&quot;:&quot;text&quot;}">Edit</a>
44
+ # </div>
45
+ # ....
46
+ # </div>
47
+ #
48
+ # If a user specifies link information for the corresponding Tandem::Content object,
49
+ # then an A HTML element will be generated surrounding the content. For example:
50
+ #
51
+ # <%= tandem_content_tag(:test_image, :image) %>
52
+ #
53
+ # produces (assuming that tandem_content.link? => true):
54
+ #
55
+ # <div class="tandem_content" id="test_image">
56
+ # ...
57
+ # <a id="tandem_content_link_test_image" class="tandem_content_link" ...
58
+ # <img id="tandem_content_image_test_image" class="tandem_content_image" ...
59
+ # </a>
60
+ # </div>
61
+ #
62
+ # tandem_content_tag accepts a hash of html_options, which will be converted to
63
+ # additional HTML attributes of the generated container div. If you specify a <tt>:class</tt> value, it will be combined
64
+ # with the default class name for your object. For example:
65
+ #
66
+ # <%= tandem_content_tag(:test_image, :image, {}, :class => "bar", :style => 'display:none') %>...
67
+ #
68
+ # produces:
69
+ #
70
+ # <div class="tandem_content bar" id="test_image" style = "display:none">...
71
+ #
72
+ # tandem_content_tag also accepts a hash of options, which will be converted to
73
+ # additional HTML attributes of the internal asset type. If you specify a <tt>:class</tt> value, it will be combined
74
+ # with the default class name for your object. For example:
75
+ #
76
+ # <%= tandem_content_tag(:test_image, :image, :class => "bar", :width => 80) %>...
77
+ #
78
+ # produces:
79
+ #
80
+ # ...<img id="tandem_content_image_test_image" class="tandem_content_image bar" width = "80"...
81
+ #
82
+ # Finally, text tandem_content_tags support an <tt>:editor</tt> option, which defaults to <tt>:plain</tt>, but
83
+ # can also be changed to <tt>:wysiwyg</tt> to enable a WYSIWYG editor, e.g.
84
+ #
85
+ # <%= tandem_content_tag(:main_body, :text, editor: :wysiwyg) %>
86
+ def tandem_content_tag(identifier, type, options = {}, html_options = {})
87
+ options[:editor] ||= :plain
88
+
89
+ using_tandem_abilities do
90
+ tandem_content = Content.scoped_type(type).constantize.find_or_create_by_request_key_and_tag(request_key, identifier)
91
+
92
+ content = case tandem_content
93
+ when Content::Text
94
+ content_tag(:div, tandem_content, options.merge(
95
+ id: "#{dom_class(tandem_content)}_#{identifier}",
96
+ class: "#{dom_class(tandem_content)} #{options[:class]}".strip
97
+ )) {
98
+ tandem_content.formatted_content.html_safe
99
+ }
100
+ when Content::Image
101
+ image_content_tag(tandem_content,options.merge(
102
+ id: "#{dom_class(tandem_content)}_#{identifier}",
103
+ class: "#{dom_class(tandem_content)} #{options[:class]}".strip
104
+ ))
105
+ else
106
+ raise "Unable to create #{tandem_content.class.name}: #{tandem_content.errors.inspect}" if tandem_content.new_record?
107
+ raise "Rendering behavior not defined for: #{tandem_content.class.name}"
108
+ end
109
+
110
+ content = link_to(content,tandem_content.link_url,{
111
+ id: "tandem_content_link_#{identifier}",
112
+ class: "tandem_content_link",
113
+ target: tandem_content.link_target
114
+ }) if tandem_content.link?
115
+
116
+ content = content_tag(:div, tandem_content, {
117
+ id: "tandem_toolbar_#{identifier}",
118
+ class: "tandem_toolbar #{options[:class]}".strip
119
+ }) {
120
+ link_to("Edit", tandem.edit_content_path(tandem_content.id, editor: options[:editor]),{
121
+ id: "tandem_edit_link_#{identifier}",
122
+ class: "tandem_edit_link #{options[:class]}".strip,
123
+ title: "editing #{identifier}"
124
+ })
125
+ } + content if can? :update, tandem_content
126
+
127
+ html_options.merge! id: identifier
128
+ html_options[:class] ||= ''
129
+ html_options[:class] << ' tandem_content' if can? :update, tandem_content
130
+ html_options[:class].strip!
131
+
132
+ content_tag(:div, tandem_content, html_options) { content }
133
+ end
134
+ end
135
+
136
+ #todo... document this
137
+ def tandem_navigation_tag(active_page, pages_collection = nil, html_options = {})
138
+ html_options, pages_collection = pages_collection, nil if pages_collection.is_a?(Hash)
139
+
140
+ html_options[:class] ||= 'nav'
141
+
142
+ page_groups = (pages_collection || Page.all).inject({}) do |groups, page|
143
+ if groups[page.parent_id.to_s]
144
+ groups[page.parent_id.to_s] << page
145
+ else
146
+ groups[page.parent_id.to_s] = [page]
147
+ end
148
+ groups
149
+ end
150
+
151
+ # generate must be in scope for the iterate proc declaration, but must be defined below iterate, so that iterate is recursively in scope
152
+ generate = nil
153
+
154
+ iterate = Proc.new do |parent_id|
155
+ #very important to delete the group from the collection, or it is possible users can create looping relationships
156
+ (page_groups.delete(parent_id.to_s) || {}).inject(''.html_safe) do |buffer, page|
157
+ buffer + generate.call(page)
158
+ end
159
+ end
160
+
161
+ generate = Proc.new do |page|
162
+ content_tag_for(:li, page, :tandem, class: "#{page == active_page ? 'active' : ''}") do
163
+ link_to(page.link_label, tandem.page_path(page), class: "#{page == active_page ? 'active' : ''}") +
164
+ content_tag(:ul) do
165
+ iterate.call(page.id)
166
+ end
167
+ end
168
+ end
169
+
170
+ content_tag(:ul, html_options) do
171
+ iterate.call(page_groups.keys.first)
172
+ end
173
+ end
174
+
175
+ def tandem_page_links(options ={})
176
+ return if cannot?(:create, @page) && cannot?(:update, @page) && cannot?(:destroy, @page)
177
+
178
+ options[:id] ||= 'tandem_page_links'
179
+
180
+ content_tag(:ul, options) do
181
+ links = []
182
+
183
+ if can?(:create, @page)
184
+ links << link_to('New Page', tandem.new_page_path(parent_id: @page.id), :class => :page_link, :id => :page_new_link)
185
+ end
186
+
187
+ if @page.persisted? && can?(:update, @page)
188
+ links << link_to('Edit Page', tandem.edit_page_path(@page), :class => :page_link, :id => :page_edit_link)
189
+ end
190
+
191
+ if @page.persisted? && can?(:destroy, @page)
192
+ links << link_to('Destroy Page', tandem.page_path(@page), :confirm => 'Are you sure?', :method => :delete)
193
+ end
194
+
195
+ if can?(:index, ::Tandem::Page)
196
+ links << link_to('Page Listing', tandem.pages_path)
197
+ end
198
+
199
+ links.collect! do |link|
200
+ content_tag(:li, link)
201
+ end
202
+
203
+ raw(links.join)
204
+ end
205
+ end
206
+
207
+ def valid_layouts
208
+ @valid_layouts ||= Dir["#{::Rails.root}/app/views/layouts/**/*.html*"].collect do |layout|
209
+ name = layout.match(/layouts\/([\w\-\/]*)((\.\w*){2})$/)[1]
210
+ name unless name == 'application'
211
+ end.compact
212
+ end
213
+
214
+ def valid_templates
215
+ @valid_templates ||= Dir["#{::Rails.root}/app/views/tandem/pages/**/*.*.*"].collect do |template|
216
+ template_name = File.basename(template).split('.').first
217
+ template_name if valid_custom_template?(template_name)
218
+ end.compact
219
+ end
220
+
221
+ private
222
+ def invalid_templates
223
+ ['show', 'edit', 'index', 'new']
224
+ end
225
+
226
+ def valid_custom_template?(template_name)
227
+ template_name[0] != '_' && !invalid_templates.include?(template_name)
228
+ end
229
+
230
+ def request_key
231
+ key = "#{controller_path}-#{action_name}".gsub(/[^\w]|_/, '-')
232
+ key += "-#{params[:id]}" if key == 'tandem-pages-show'
233
+ key
234
+ end
235
+
236
+ def using_tandem_abilities
237
+ controller.instance_variable_set :@current_ability, ::Tandem::Ability.new(::Tandem::Configuration.current_user.call(request))
238
+ yield.tap do
239
+ controller.instance_variable_set :@current_ability, nil
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,6 @@
1
+ module Tandem
2
+ class Ability
3
+ include CanCan::Ability
4
+ define_method :initialize, Configuration.user_abilities
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ module Tandem
2
+ class Content::Image < Content
3
+ #todo: the following line introduces an artifact field on the other types of Content. For now there is only one other field type, so this down and dirty association is quickest.
4
+ #todo: I've already added a polymorphic field "attachment" that is designed for this same purpose, but is currently not being used for anything, it should be removed as well^^.
5
+ belongs_to :image, class_name: 'Tandem::Image'
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Tandem
2
+ class Content::Text < Content
3
+ def formatted_content
4
+ content.presence || '<p>Sample Content</p>'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,41 @@
1
+ module Tandem
2
+ class Content < ActiveRecord::Base
3
+ validates :tag, presence: true
4
+ validates :request_key, presence: true, uniqueness: {:scope => [:tag, :type]}
5
+
6
+
7
+ #enforce abstract class architecture
8
+ validates :type, presence: true, exclusion: ['Tandem::Content']
9
+
10
+ attr_accessible :tag, :content, :details, :link_url, :link_target, :attachment_id, :attachment_type, :image_id, :request_key
11
+
12
+ def self.subclass_names
13
+ @subclass_names ||= subclasses.map { |sc| sc.name }
14
+ end
15
+
16
+ def self.simple_types
17
+ @simple_types ||= subclasses.map { |sc| sc.simple_type }
18
+ end
19
+
20
+ def self.valid_simple_type?(type)
21
+ simple_types.include?(type.to_s.downcase.to_sym)
22
+ end
23
+
24
+ def self.simple_type
25
+ name =~ /^Tandem\:\:Content\:\:(.*)/
26
+ ($1 or raise "Unable to determine simple type for abstract class: #{name}").downcase.to_sym
27
+ end
28
+
29
+ def self.scoped_type(type)
30
+ valid_simple_type?(type) or raise "Invalid Tandem::Content.simple_type: '#{type}'. Valid options #{simple_types.join(', ')}"
31
+ "Tandem::Content::#{type.to_s.camelize}"
32
+ end
33
+
34
+ def link?
35
+ link_url.present?
36
+ end
37
+ end
38
+ end
39
+
40
+ load 'image.rb'
41
+ load 'text.rb'
@@ -0,0 +1,27 @@
1
+ module Tandem
2
+ class Image < ActiveRecord::Base
3
+
4
+ THUMB_WIDTH = 150
5
+ THUMB_HEIGHT = 150
6
+
7
+ has_attached_file :resource, Tandem::Configuration.paperclip_options
8
+
9
+ validates_attachment_presence :resource
10
+ validates_attachment_size :resource, :less_than => 1.megabyte
11
+ validates_attachment_content_type :resource, :content_type => ['image/gif', 'image/jpg', 'image/jpeg', 'image/png']
12
+
13
+ has_many :content_images, class_name: 'Tandem::Content::Image'
14
+
15
+ default_scope order('created_at DESC')
16
+
17
+ attr_accessible :resource
18
+
19
+ def thumb_url
20
+ resource.url(:thumb)
21
+ end
22
+
23
+ def as_json(options={})
24
+ super(:methods => :thumb_url)
25
+ end
26
+ end
27
+ end