thesis 0.0.1 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +147 -7
- data/lib/assets/javascripts/thesis/hallo.js +2949 -0
- data/lib/assets/javascripts/thesis/thesis.js.coffee +27 -0
- data/lib/assets/javascripts/thesis.js +2 -0
- data/lib/assets/stylesheets/thesis/thesis.css.scss +16 -0
- data/lib/assets/stylesheets/thesis.css +4 -0
- data/lib/generators/thesis/install/install_generator.rb +108 -0
- data/lib/generators/thesis/install/templates/migrations/create_page.rb +18 -0
- data/lib/generators/thesis/install/templates/migrations/create_page_content.rb +15 -0
- data/lib/generators/thesis/install/templates/page_templates/default.html.erb +38 -0
- data/lib/generators/thesis/install/templates/page_templates/default.html.haml +29 -0
- data/lib/generators/thesis/install/templates/page_templates/default.html.slim +29 -0
- data/lib/tasks/thesis_tasks.rake +4 -0
- data/lib/thesis/colorizer.rb +51 -0
- data/lib/thesis/controllers/controller_helpers.rb +39 -0
- data/lib/thesis/controllers/thesis_controller.rb +53 -0
- data/lib/thesis/engine.rb +5 -0
- data/lib/thesis/exceptions.rb +17 -0
- data/lib/thesis/models/page.rb +43 -0
- data/lib/thesis/models/page_content.rb +41 -0
- data/lib/thesis/railtie.rb +12 -0
- data/lib/thesis/routing/route_constraint.rb +7 -0
- data/lib/thesis/routing/routes.rb +10 -0
- data/lib/thesis/version.rb +1 -1
- data/lib/thesis.rb +10 -1
- data/spec/factories/page_contents.rb +7 -0
- data/spec/factories/pages.rb +9 -0
- data/spec/lib/thesis/controllers/thesis_controller_spec.rb +68 -0
- data/spec/lib/thesis/models/page_content_spec.rb +33 -0
- data/spec/lib/thesis/models/page_spec.rb +49 -0
- data/spec/spec_helper.rb +64 -0
- metadata +58 -14
- data/.gitignore +0 -17
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -22
- data/Rakefile +0 -1
- data/lib/thesis/base.rb +0 -5
- data/thesis.gemspec +0 -19
@@ -0,0 +1,27 @@
|
|
1
|
+
Thesis =
|
2
|
+
setup: ->
|
3
|
+
if this.requirements()
|
4
|
+
this.bindings()
|
5
|
+
|
6
|
+
|
7
|
+
requirements: ->
|
8
|
+
if jQuery.ui
|
9
|
+
true
|
10
|
+
else
|
11
|
+
alert "jQuery UI not included. Thesis will not work properly without it."
|
12
|
+
false
|
13
|
+
|
14
|
+
page_is_editable: ->
|
15
|
+
$("#thesis-editor").length > 0
|
16
|
+
|
17
|
+
bindings: ->
|
18
|
+
thesis = $("#thesis-editor")
|
19
|
+
if thesis
|
20
|
+
thesis.append $("<div></div>").addClass("thesis-container")
|
21
|
+
.append $("<h3></h3>").text("Thesis Editor")
|
22
|
+
|
23
|
+
|
24
|
+
jQuery ($)->
|
25
|
+
Thesis.setup()
|
26
|
+
|
27
|
+
# window.Thesis = Thesis # Enable if necessary
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'rails/generators/migration'
|
2
|
+
|
3
|
+
module Thesis
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < ::Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
source_root File.expand_path('../templates', __FILE__)
|
9
|
+
|
10
|
+
desc "install or upgrade Thesis"
|
11
|
+
|
12
|
+
def self.next_migration_number(path)
|
13
|
+
unless @prev_migration_nr
|
14
|
+
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
15
|
+
else
|
16
|
+
@prev_migration_nr += 1
|
17
|
+
end
|
18
|
+
@prev_migration_nr.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def copy_migrations
|
22
|
+
migration_template "migrations/create_page.rb", "db/migrate/create_page.rb"
|
23
|
+
migration_template "migrations/create_page_content.rb", "db/migrate/create_page_content.rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_folders
|
27
|
+
copy_file "page_templates/default.html.slim", "app/views/page_templates/default.html.slim" if Slim
|
28
|
+
copy_file "page_templates/default.html.haml", "app/views/page_templates/default.html.haml" if Haml
|
29
|
+
copy_file "page_templates/default.html.erb", "app/views/page_templates/default.html.erb" unless Haml || Slim
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_up_routes
|
33
|
+
route "thesis_routes # This needs to be the last route!"
|
34
|
+
end
|
35
|
+
|
36
|
+
def install_js
|
37
|
+
filename = "app/assets/javascripts/application.js"
|
38
|
+
existing = File.binread("#{filename}").include?("require thesis")
|
39
|
+
|
40
|
+
if existing && generating?
|
41
|
+
say_status("skipped", "insert into #{filename}", :yellow)
|
42
|
+
else
|
43
|
+
insert_into_file "#{filename}", after: %r{//= require +['"]?jquery_ujs['"]?} do
|
44
|
+
"\n//= require jquery-ui" +
|
45
|
+
"\n//= require thesis"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def install_css
|
51
|
+
filename = "app/assets/stylesheets/application.css.scss"
|
52
|
+
existing = File.binread("#{filename}").include?("require thesis")
|
53
|
+
|
54
|
+
if existing && generating?
|
55
|
+
say_status("skipped", "insert into #{filename}", :yellow)
|
56
|
+
else
|
57
|
+
insert_into_file "#{filename}", after: %r{ *= require_self} do
|
58
|
+
"\n *= require thesis"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def install_page_is_editable
|
64
|
+
filename = "app/controllers/application_controller.rb"
|
65
|
+
existing = File.binread("#{filename}").include?("def page_is_editable?")
|
66
|
+
|
67
|
+
if existing && generating?
|
68
|
+
say_status("skipped", "insert into #{filename}", :yellow)
|
69
|
+
else
|
70
|
+
insert_into_file "#{filename}", after: %r{ protect_from_forgery} do
|
71
|
+
"\n" +
|
72
|
+
"\n # Thesis authentication" +
|
73
|
+
"\n def page_is_editable?(page)" +
|
74
|
+
"\n # Add your own criteria here for editing privileges. Examples:" +
|
75
|
+
"\n # current_user.admin? # Basic admin" +
|
76
|
+
"\n # can? :update, page # CanCan" +
|
77
|
+
"\n true # EVERYONE has access right now." +
|
78
|
+
"\n end"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def complete_message
|
84
|
+
require "thesis/colorizer"
|
85
|
+
|
86
|
+
if generating?
|
87
|
+
puts " "
|
88
|
+
puts " Thesis installed.".green
|
89
|
+
puts " Now run `rake db:migrate` to set up your database.".pink
|
90
|
+
else
|
91
|
+
puts " "
|
92
|
+
puts " Thesis uninstalled.".red
|
93
|
+
puts " You will need to remove the database tables manually if you've already run `rake db:migrate`.".pink
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
|
99
|
+
def generating?
|
100
|
+
:invoke == behavior
|
101
|
+
end
|
102
|
+
|
103
|
+
def destroying?
|
104
|
+
:revoke == behavior
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreatePage < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :pages do |t|
|
4
|
+
t.integer :parent_id
|
5
|
+
t.string :name
|
6
|
+
t.string :slug
|
7
|
+
t.string :title
|
8
|
+
t.string :description
|
9
|
+
t.integer :sort_order, default: 0, null: false
|
10
|
+
t.string :template, default: "default", null: false
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :pages
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreatePageContent < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :page_contents do |t|
|
4
|
+
t.integer :page_id, null: false
|
5
|
+
t.string :name, null: false
|
6
|
+
t.text :content, default: "Edit This Content Area"
|
7
|
+
t.string :content_type, default: :html
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.down
|
13
|
+
drop_table :page_contents
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= current_page.title %></title>
|
5
|
+
<meta type="description" content="<%= current_page.content("Description", :text) %>" />
|
6
|
+
|
7
|
+
<%= stylesheet_link_tag "application", :media => "all" %>
|
8
|
+
<%= javascript_include_tag "application" %>
|
9
|
+
<%= csrf_meta_tags %>
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<%= thesis_editor %>
|
13
|
+
<header>
|
14
|
+
<h1><%= current_page.title %></h1>
|
15
|
+
</header>
|
16
|
+
|
17
|
+
<nav>
|
18
|
+
<ul>
|
19
|
+
<% root_pages.each do |p| %>
|
20
|
+
<li><%= link_to p.name, p.path %></li>
|
21
|
+
<% end %>
|
22
|
+
</ul>
|
23
|
+
</nav>
|
24
|
+
|
25
|
+
<article>
|
26
|
+
<div class="main-image"><%= current_page.content("Main Image", :image) %></div>
|
27
|
+
<div class="content"><%= current_page.content("Main Content", :html) %></div>
|
28
|
+
</article>
|
29
|
+
|
30
|
+
<aside>
|
31
|
+
<%= current_page.content("Sidebar Content", :html) %>
|
32
|
+
</aside>
|
33
|
+
|
34
|
+
<footer>
|
35
|
+
<p><%= current_page.content("Footer Content", :text) %></p>
|
36
|
+
</footer>
|
37
|
+
</body>
|
38
|
+
</html>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title= current_page.title
|
5
|
+
%meta{ content: current_page.content("Description", :text), type: "description" }
|
6
|
+
|
7
|
+
= stylesheet_link_tag "application", :media => "all"
|
8
|
+
= javascript_include_tag "application"
|
9
|
+
= csrf_meta_tags
|
10
|
+
%body
|
11
|
+
= thesis_editor
|
12
|
+
|
13
|
+
%header
|
14
|
+
%h1= current_page.title
|
15
|
+
|
16
|
+
%nav
|
17
|
+
%ul
|
18
|
+
- root_pages.each do |p|
|
19
|
+
%li= link_to p.name, p.path
|
20
|
+
|
21
|
+
%article
|
22
|
+
.main-image= current_page.content("Main Image", :image)
|
23
|
+
.content= current_page.content("Main Content", :html)
|
24
|
+
|
25
|
+
%aside
|
26
|
+
= current_page.content("Sidebar Content", :html)
|
27
|
+
|
28
|
+
%footer
|
29
|
+
%p= current_page.content("Footer Content", :text)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
doctype html
|
2
|
+
html
|
3
|
+
head
|
4
|
+
title= current_page.title
|
5
|
+
meta content=current_page.content("Description", :text) type="description"
|
6
|
+
|
7
|
+
= stylesheet_link_tag "application", :media => "all"
|
8
|
+
= javascript_include_tag "application"
|
9
|
+
= csrf_meta_tags
|
10
|
+
body
|
11
|
+
= thesis_editor
|
12
|
+
|
13
|
+
header
|
14
|
+
h1= current_page.title
|
15
|
+
|
16
|
+
nav
|
17
|
+
ul
|
18
|
+
- root_pages.each do |p|
|
19
|
+
li= link_to p.name, p.path
|
20
|
+
|
21
|
+
article
|
22
|
+
.main-image= current_page.content("Main Image", :image)
|
23
|
+
.content= current_page.content("Main Content", :html)
|
24
|
+
|
25
|
+
aside
|
26
|
+
= current_page.content("Sidebar Content", :html)
|
27
|
+
|
28
|
+
footer
|
29
|
+
p= current_page.content("Footer Content", :text)
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class String
|
2
|
+
# colorization
|
3
|
+
def colorize(color_code)
|
4
|
+
"\e[#{color_code}m#{self}\e[0m"
|
5
|
+
end
|
6
|
+
|
7
|
+
def red
|
8
|
+
colorize 31
|
9
|
+
end
|
10
|
+
|
11
|
+
def green
|
12
|
+
colorize 32
|
13
|
+
end
|
14
|
+
|
15
|
+
def yellow
|
16
|
+
colorize 33
|
17
|
+
end
|
18
|
+
|
19
|
+
def pink
|
20
|
+
colorize 35
|
21
|
+
end
|
22
|
+
|
23
|
+
def gray
|
24
|
+
colorize 37
|
25
|
+
end
|
26
|
+
|
27
|
+
def brown
|
28
|
+
colorize 33
|
29
|
+
end
|
30
|
+
|
31
|
+
def blue
|
32
|
+
colorize 34
|
33
|
+
end
|
34
|
+
|
35
|
+
def magenta
|
36
|
+
colorize 35
|
37
|
+
end
|
38
|
+
|
39
|
+
def cyan
|
40
|
+
colorize 36
|
41
|
+
end
|
42
|
+
|
43
|
+
def bold
|
44
|
+
colorize 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def blink
|
48
|
+
colorize 5
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Thesis
|
2
|
+
module ControllerHelpers
|
3
|
+
module ClassMethods
|
4
|
+
def class_method_here
|
5
|
+
# Sample
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
raise ActiveRecordRequired.new("Currently, Thesis only works with ActiveRecord.") unless defined? ActiveRecord
|
11
|
+
|
12
|
+
# base.extend ClassMethods
|
13
|
+
# base.helper_method :class_method_here
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_page
|
17
|
+
@current_page ||= Page.where(slug: request.fullpath).first
|
18
|
+
end
|
19
|
+
|
20
|
+
def root_pages
|
21
|
+
@root_pages ||= Page.where(parent_id: nil).order("sort_order ASC").all
|
22
|
+
end
|
23
|
+
|
24
|
+
def page_is_editable?(page)
|
25
|
+
raise RequiredMethodNotImplemented.new("Add a `page_is_editable?(page)` method to your controller that returns true or false.")
|
26
|
+
end
|
27
|
+
|
28
|
+
def thesis_editor
|
29
|
+
"<div id='thesis-editor'></div>".html_safe if page_is_editable?(current_page)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if defined? ActionController::Base
|
35
|
+
ActionController::Base.class_eval do
|
36
|
+
include Thesis::ControllerHelpers
|
37
|
+
helper_method :current_page, :root_pages, :page_is_editable?, :thesis_editor
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Thesis
|
2
|
+
class ThesisController < ActionController::Base
|
3
|
+
include ControllerHelpers
|
4
|
+
|
5
|
+
def show
|
6
|
+
raise ActionController::RoutingError.new('Not Found') unless current_page
|
7
|
+
|
8
|
+
if current_page.template && template_exists?("page_templates/#{current_page.template}")
|
9
|
+
render "page_templates/#{current_page.template}"
|
10
|
+
else
|
11
|
+
raise PageRequiresTemplate.new("Page requires a template but none was specified.")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def new_page
|
16
|
+
page = Page.new
|
17
|
+
return head :forbidden unless page_is_editable?(page)
|
18
|
+
|
19
|
+
update_page_attributes page
|
20
|
+
|
21
|
+
head page.save ? :ok : :not_acceptable
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_page
|
25
|
+
page = current_page
|
26
|
+
return head :forbidden unless page_is_editable?(page)
|
27
|
+
|
28
|
+
update_page_attributes page
|
29
|
+
|
30
|
+
head page.save ? :ok : :not_acceptable
|
31
|
+
end
|
32
|
+
|
33
|
+
def page_attributes
|
34
|
+
[ :name, :title, :description, :parent_id ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_page_attributes(page)
|
38
|
+
page_attributes.each { |a| page.send("#{a}=", params[a]) if params[a] }
|
39
|
+
page
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def page_is_editable?(page)
|
45
|
+
appcon = ApplicationController.new
|
46
|
+
if appcon.respond_to?("page_is_editable?")
|
47
|
+
appcon.page_is_editable?(page)
|
48
|
+
else
|
49
|
+
raise RequiredMethodNotImplemented.new("Add a `page_is_editable?(page)` method to your controller that returns true or false.")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Thesis
|
2
|
+
# A generic Thesis exception
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
# When a required method is not implemented
|
6
|
+
class RequiredMethodNotImplemented < StandardError; end
|
7
|
+
|
8
|
+
# ActiveRecord is the only ORM that works with this currently
|
9
|
+
class ActiveRecordRequired < StandardError; end
|
10
|
+
|
11
|
+
# 404s
|
12
|
+
# class PageNotFound < ActionController::RoutingError; end
|
13
|
+
|
14
|
+
# No template specified
|
15
|
+
class PageRequiresTemplate < StandardError; end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Thesis
|
2
|
+
class Page < ActiveRecord::Base
|
3
|
+
self.table_name = "pages"
|
4
|
+
|
5
|
+
belongs_to :parent, class_name: "Page"
|
6
|
+
has_many :subpages, class_name: "Page", foreign_key: "parent_id", order: "sort_order ASC"
|
7
|
+
has_many :page_contents
|
8
|
+
|
9
|
+
before_validation :update_slug
|
10
|
+
after_save :update_subpage_slugs
|
11
|
+
|
12
|
+
validates :slug, uniqueness: { message: "There's already a page like that. Change your page name." }, presence: true
|
13
|
+
|
14
|
+
def update_slug
|
15
|
+
self.slug = "/" << self.name.parameterize
|
16
|
+
self.slug = "#{parent.slug.to_s}#{self.slug.to_s}" if parent
|
17
|
+
end
|
18
|
+
|
19
|
+
def update_subpage_slugs
|
20
|
+
subpages.each(&:save) if slug_changed?
|
21
|
+
end
|
22
|
+
|
23
|
+
def content(name, content_type = :html)
|
24
|
+
pc = find_or_create_page_content(name, content_type)
|
25
|
+
pc.render
|
26
|
+
end
|
27
|
+
|
28
|
+
def path
|
29
|
+
self.slug
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def find_or_create_page_content(name, content_type)
|
35
|
+
page_content = self.page_contents.where(name: name).first_or_create do |pc|
|
36
|
+
pc.content = "Edit This Area"
|
37
|
+
end
|
38
|
+
page_content.content_type = content_type
|
39
|
+
page_content.save if page_content.changed?
|
40
|
+
page_content
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Thesis
|
2
|
+
class PageContent < ActiveRecord::Base
|
3
|
+
self.table_name = "page_contents"
|
4
|
+
|
5
|
+
belongs_to :page
|
6
|
+
|
7
|
+
def render
|
8
|
+
case self.content_type.to_sym
|
9
|
+
when :html
|
10
|
+
render_html
|
11
|
+
when :text
|
12
|
+
render_text
|
13
|
+
else
|
14
|
+
render_html
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def render_html
|
21
|
+
h = "<div class='thesis-content thesis-content-html' data-thesis-content-id='#{self.id}'>" +
|
22
|
+
"#{self.content}" +
|
23
|
+
"</div>"
|
24
|
+
h.html_safe
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_text
|
28
|
+
h = "<span class='thesis-content thesis-content-text' data-thesis-content-id='#{self.id}'>" +
|
29
|
+
"#{self.content}" +
|
30
|
+
"</span>"
|
31
|
+
h.html_safe
|
32
|
+
end
|
33
|
+
|
34
|
+
def render_image
|
35
|
+
h = "<div class='thesis-content thesis-content-image'>" +
|
36
|
+
"<img src='#{self.content}' />" +
|
37
|
+
"</div>"
|
38
|
+
h.html_safe
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/thesis/version.rb
CHANGED
data/lib/thesis.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
|
+
require "thesis/engine"
|
1
2
|
require "thesis/version"
|
2
|
-
require "thesis/
|
3
|
+
require "thesis/exceptions"
|
4
|
+
require "thesis/controllers/controller_helpers" # Included into ActionController::Base
|
5
|
+
require "thesis/controllers/thesis_controller"
|
6
|
+
require "thesis/routing/route_constraint"
|
7
|
+
require "thesis/routing/routes"
|
8
|
+
|
9
|
+
# Models
|
10
|
+
require "thesis/models/page"
|
11
|
+
require "thesis/models/page_content"
|
3
12
|
|
4
13
|
module Thesis
|
5
14
|
end
|