tandem 0.2.0.rc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +205 -0
- data/Rakefile +31 -0
- data/app/assets/images/tandem/blank_image.jpg +0 -0
- data/app/assets/images/tandem/colorbox/border1.png +0 -0
- data/app/assets/images/tandem/colorbox/border2.png +0 -0
- data/app/assets/images/tandem/colorbox/ie6/borderBottomCenter.png +0 -0
- data/app/assets/images/tandem/colorbox/ie6/borderBottomLeft.png +0 -0
- data/app/assets/images/tandem/colorbox/ie6/borderBottomRight.png +0 -0
- data/app/assets/images/tandem/colorbox/ie6/borderMiddleLeft.png +0 -0
- data/app/assets/images/tandem/colorbox/ie6/borderMiddleRight.png +0 -0
- data/app/assets/images/tandem/colorbox/ie6/borderTopCenter.png +0 -0
- data/app/assets/images/tandem/colorbox/ie6/borderTopLeft.png +0 -0
- data/app/assets/images/tandem/colorbox/ie6/borderTopRight.png +0 -0
- data/app/assets/images/tandem/colorbox/loading.gif +0 -0
- data/app/assets/images/tandem/ic-delete_image.png +0 -0
- data/app/assets/images/tandem/ic-star.png +0 -0
- data/app/assets/images/tandem/loader.gif +0 -0
- data/app/assets/images/tandem/loader_stripe.png +0 -0
- data/app/assets/images/tandem/tandem-editImage2.jpg +0 -0
- data/app/assets/images/tandem/tandem_logo.png +0 -0
- data/app/assets/images/tandem/tandem_logo_nav.png +0 -0
- data/app/assets/javascripts/tandem/contents.js +2 -0
- data/app/assets/javascripts/tandem/current_image.jst.eco +19 -0
- data/app/assets/javascripts/tandem/gallery_image.jst.eco +10 -0
- data/app/assets/javascripts/tandem/images.coffee +42 -0
- data/app/assets/javascripts/tandem/pages.js.coffee +32 -0
- data/app/assets/javascripts/tandem/popup.js +23 -0
- data/app/assets/javascripts/tandem.js +16 -0
- data/app/assets/stylesheets/tandem/colorbox.css.erb +83 -0
- data/app/assets/stylesheets/tandem/contents.scss +541 -0
- data/app/assets/stylesheets/tandem/scaffold.scss +61 -0
- data/app/assets/stylesheets/tandem/variables.scss +38 -0
- data/app/assets/stylesheets/tandem.css +7 -0
- data/app/controllers/tandem/application_controller.rb +19 -0
- data/app/controllers/tandem/contents_controller.rb +94 -0
- data/app/controllers/tandem/images_controller.rb +92 -0
- data/app/controllers/tandem/pages_controller.rb +108 -0
- data/app/helpers/tandem/application_helper.rb +29 -0
- data/app/helpers/tandem/contents_helper.rb +19 -0
- data/app/helpers/tandem/images_helper.rb +10 -0
- data/app/helpers/tandem/pages_helper.rb +243 -0
- data/app/models/tandem/ability.rb +6 -0
- data/app/models/tandem/content/image.rb +7 -0
- data/app/models/tandem/content/text.rb +7 -0
- data/app/models/tandem/content.rb +41 -0
- data/app/models/tandem/image.rb +27 -0
- data/app/models/tandem/page.rb +41 -0
- data/app/views/layouts/tandem/image.html.slim +19 -0
- data/app/views/layouts/tandem/popup.html.slim +17 -0
- data/app/views/tandem/contents/_form.html.slim +36 -0
- data/app/views/tandem/contents/edit.html.slim +3 -0
- data/app/views/tandem/contents/index.html.slim +29 -0
- data/app/views/tandem/contents/new.html.slim +3 -0
- data/app/views/tandem/contents/show.html.slim +20 -0
- data/app/views/tandem/images/_form.html.slim +24 -0
- data/app/views/tandem/images/_gallery.html.slim +4 -0
- data/app/views/tandem/images/edit.html.slim +8 -0
- data/app/views/tandem/images/index.html.slim +9 -0
- data/app/views/tandem/images/new.html.slim +4 -0
- data/app/views/tandem/images/show.html.slim +10 -0
- data/app/views/tandem/images/thumb.html.slim +1 -0
- data/app/views/tandem/pages/_form.html.slim +56 -0
- data/app/views/tandem/pages/edit.html.slim +1 -0
- data/app/views/tandem/pages/index.html.slim +35 -0
- data/app/views/tandem/pages/new.html.slim +1 -0
- data/app/views/tandem/pages/show.html.slim +16 -0
- data/config/cucumber.yml +8 -0
- data/config/routes.rb +17 -0
- data/db/migrate/20111122221549_create_tandem_pages.rb +20 -0
- data/db/migrate/20111122222037_create_tandem_contents.rb +21 -0
- data/db/migrate/20111215001943_create_tandem_images.rb +12 -0
- data/db/migrate/30000000000000_create_default_page.rb +10 -0
- data/db/migrate/30000000000001_add_request_key_to_tandem_contents.rb +44 -0
- data/lib/generators/tandem_generator.rb +26 -0
- data/lib/generators/templates/initializer.rb +70 -0
- data/lib/tandem/engine.rb +23 -0
- data/lib/tandem/version.rb +3 -0
- data/lib/tandem.rb +46 -0
- data/lib/tasks/cucumber.rake +65 -0
- data/lib/tasks/tandem_tasks.rake +4 -0
- 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="{"identifier":"test_text","type":"text"}">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,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,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
|