content_engine 0.1.0
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/.gitignore +4 -0
- data/Gemfile +40 -0
- data/README.md +43 -0
- data/Rakefile +86 -0
- data/VERSION +1 -0
- data/app/controllers/content_engine/articles_controller.rb +54 -0
- data/app/controllers/content_engine/pages_controller.rb +76 -0
- data/app/controllers/content_engine_controller.rb +14 -0
- data/app/helpers/application_helper.rb +2 -0
- data/app/models/article.rb +29 -0
- data/app/models/content.rb +64 -0
- data/app/models/page.rb +40 -0
- data/app/models/site.rb +26 -0
- data/app/models/tag.rb +53 -0
- data/app/models/user.rb +5 -0
- data/app/views/content_engine/articles/_form.erb +8 -0
- data/app/views/content_engine/articles/edit.html.erb +7 -0
- data/app/views/content_engine/articles/index.html.erb +10 -0
- data/app/views/content_engine/articles/new.html.erb +6 -0
- data/app/views/content_engine/articles/show.html.erb +9 -0
- data/app/views/content_engine/pages/_form.erb +8 -0
- data/app/views/content_engine/pages/edit.html.erb +7 -0
- data/app/views/content_engine/pages/index.html.erb +10 -0
- data/app/views/content_engine/pages/new.html.erb +6 -0
- data/app/views/content_engine/pages/show.html.erb +9 -0
- data/app/views/layouts/application.html.erb +16 -0
- data/config/application.rb +1 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +25 -0
- data/content_engine.gemspec +167 -0
- data/db/migrate/20100225105011_create_contents.rb +22 -0
- data/db/migrate/20100225105903_create_users.rb +8 -0
- data/db/migrate/20100225110024_create_sites.rb +15 -0
- data/db/migrate/20100225110037_create_memberships.rb +12 -0
- data/db/migrate/20100225112236_acts_as_taggable_on.rb +26 -0
- data/db/schema.rb +58 -0
- data/lib/content_engine.rb +24 -0
- data/lib/tasks/content_engine.rake +24 -0
- data/spec/blueprint.rb +41 -0
- data/spec/models/article_spec.rb +56 -0
- data/spec/models/page_spec.rb +158 -0
- data/spec/models/site_spec.rb +32 -0
- data/spec/rails_app/.gitignore +4 -0
- data/spec/rails_app/Rakefile +10 -0
- data/spec/rails_app/app/controllers/admins_controller.rb +6 -0
- data/spec/rails_app/app/controllers/application_controller.rb +6 -0
- data/spec/rails_app/app/controllers/home_controller.rb +4 -0
- data/spec/rails_app/app/controllers/sessions_controller.rb +6 -0
- data/spec/rails_app/app/controllers/users_controller.rb +12 -0
- data/spec/rails_app/app/helpers/application_helper.rb +3 -0
- data/spec/rails_app/app/views/home/index.html.erb +1 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +19 -0
- data/spec/rails_app/config/application.rb +30 -0
- data/spec/rails_app/config/boot.rb +9 -0
- data/spec/rails_app/config/database.yml +16 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +19 -0
- data/spec/rails_app/config/environments/production.rb +33 -0
- data/spec/rails_app/config/environments/test.rb +29 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/cookie_verification_secret.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +2 -0
- data/spec/rails_app/config/initializers/session_store.rb +15 -0
- data/spec/rails_app/config/routes.rb +21 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/db/migrate/20100225105011_create_contents.rb +22 -0
- data/spec/rails_app/db/migrate/20100225105903_create_users.rb +8 -0
- data/spec/rails_app/db/migrate/20100225110024_create_sites.rb +15 -0
- data/spec/rails_app/db/migrate/20100225110037_create_memberships.rb +12 -0
- data/spec/rails_app/db/migrate/20100225112236_acts_as_taggable_on.rb +26 -0
- data/spec/rails_app/db/schema.rb +59 -0
- data/spec/rails_app/public/404.html +26 -0
- data/spec/rails_app/public/422.html +26 -0
- data/spec/rails_app/public/500.html +26 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/images/rails.png +0 -0
- data/spec/rails_app/public/javascripts/application.js +2 -0
- data/spec/rails_app/public/javascripts/controls.js +963 -0
- data/spec/rails_app/public/javascripts/dragdrop.js +973 -0
- data/spec/rails_app/public/javascripts/effects.js +1128 -0
- data/spec/rails_app/public/javascripts/prototype.js +4320 -0
- data/spec/rails_app/public/javascripts/rails.js +110 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/public/stylesheets/.gitkeep +0 -0
- data/spec/rails_app/script/rails +10 -0
- data/spec/requests/user_manage_articles_spec.rb +85 -0
- data/spec/requests/user_manage_pages_spec.rb +92 -0
- data/spec/requests/webrat.log +17 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +36 -0
- metadata +226 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Edit this Gemfile to bundle your application's dependencies.
|
|
2
|
+
source 'http://gemcutter.org'
|
|
3
|
+
|
|
4
|
+
gem "rails", "3.0.0.beta"
|
|
5
|
+
|
|
6
|
+
## Bundle edge rails:
|
|
7
|
+
# gem "rails", :git => "git://github.com/rails/rails.git"
|
|
8
|
+
|
|
9
|
+
# ActiveRecord requires a database adapter. By default,
|
|
10
|
+
# Rails has selected sqlite3.
|
|
11
|
+
gem "sqlite3-ruby", :require => "sqlite3"
|
|
12
|
+
|
|
13
|
+
# Application dependencies
|
|
14
|
+
|
|
15
|
+
gem "will_paginate"
|
|
16
|
+
gem "rdiscount"
|
|
17
|
+
gem "formtastic"
|
|
18
|
+
gem "permalink_fu" , :git => 'git://github.com/idealian/permalink_fu.git'
|
|
19
|
+
gem "acts-as-taggable-on", :git => 'git://github.com/fousa/acts-as-taggable-on.git'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
group :development do
|
|
23
|
+
gem "launchy"
|
|
24
|
+
gem "jeweler"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
group :cucumber do
|
|
28
|
+
gem 'webrat', :require => nil
|
|
29
|
+
gem 'cucumber-rails', :git => 'git://github.com/aslakhellesoy/cucumber-rails.git'
|
|
30
|
+
gem 'rspec-rails', :git => 'git://github.com/rspec/rspec-rails.git'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# bundler requires these gems while running tests
|
|
34
|
+
group :test do
|
|
35
|
+
gem 'test-unit', "1.2.3"
|
|
36
|
+
gem 'machinist', :git => 'git://github.com/notahat/machinist.git'
|
|
37
|
+
gem 'rspec-rails', :git => 'git://github.com/rspec/rspec-rails.git'
|
|
38
|
+
gem 'webrat', :require => nil
|
|
39
|
+
gem 'faker', :require => nil
|
|
40
|
+
end
|
data/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Content Engine
|
|
2
|
+
===
|
|
3
|
+
|
|
4
|
+
Rails3 Engine for awesome Site and content management
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Dependencies
|
|
8
|
+
---
|
|
9
|
+
- Rails 3
|
|
10
|
+
- Idealian UserEngine or existence of users table
|
|
11
|
+
|
|
12
|
+
Install
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
Add following to your Gemfile
|
|
16
|
+
|
|
17
|
+
gem "permalink_fu" , :git => 'git://github.com/idealian/permalink_fu.git'
|
|
18
|
+
gem "acts-as-taggable-on", :git => 'git://github.com/fousa/acts-as-taggable-on.git'
|
|
19
|
+
gem "content_engine", :git => "git@github.com:idealian/content_engine.git"
|
|
20
|
+
|
|
21
|
+
And run `bundle install`
|
|
22
|
+
|
|
23
|
+
Features
|
|
24
|
+
---
|
|
25
|
+
sites
|
|
26
|
+
pages
|
|
27
|
+
articles
|
|
28
|
+
tags
|
|
29
|
+
|
|
30
|
+
Customization
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
To override a controller
|
|
34
|
+
|
|
35
|
+
class PagesController < ContentEngine::PagesController
|
|
36
|
+
def index
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def show
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Copyright (c) 2010 Taylor Luk, Idealian Pty Ltd All right reserved
|
data/Rakefile
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
|
2
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
|
3
|
+
|
|
4
|
+
require File.expand_path('../spec/rails_app/config/application', __FILE__)
|
|
5
|
+
|
|
6
|
+
require 'rake'
|
|
7
|
+
require 'rake/testtask'
|
|
8
|
+
require 'rake/rdoctask'
|
|
9
|
+
|
|
10
|
+
# gem "will_paginate"
|
|
11
|
+
# gem "rdiscount"
|
|
12
|
+
# gem "formtastic"
|
|
13
|
+
# gem "permalink_fu", :git => 'git://github.com/idealian/permalink_fu.git'
|
|
14
|
+
# gem "acts-as-taggable-on", :git => 'git://github.com/fousa/acts-as-taggable-on.git'
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
require 'jeweler'
|
|
18
|
+
Jeweler::Tasks.new do |gem|
|
|
19
|
+
gem.name = "content_engine"
|
|
20
|
+
gem.summary = %Q{A Rails 3 Engine for sexier site and content management}
|
|
21
|
+
gem.description = %Q{Extracted from shop2, site-rest}
|
|
22
|
+
gem.email = "taylor.luk@idealian.net"
|
|
23
|
+
gem.homepage = "http://github.com/idealian/content_engine"
|
|
24
|
+
gem.authors = ["Taylor Luk"]
|
|
25
|
+
gem.add_dependency "will_paginate"
|
|
26
|
+
gem.add_dependency "rdiscount"
|
|
27
|
+
gem.add_dependency "formtastic"
|
|
28
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
|
29
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
|
30
|
+
end
|
|
31
|
+
Jeweler::GemcutterTasks.new
|
|
32
|
+
rescue LoadError
|
|
33
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
require 'rspec/core'
|
|
38
|
+
require 'rspec/core/rake_task'
|
|
39
|
+
rescue MissingSourceFile
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
task :default => :spec
|
|
43
|
+
|
|
44
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
|
45
|
+
Rspec::Core::RakeTask.new(:spec)
|
|
46
|
+
|
|
47
|
+
namespace :spec do
|
|
48
|
+
[:requests, :models, :controllers, :views, :helpers, :mailers, :lib].each do |sub|
|
|
49
|
+
desc "Run the code examples in spec/#{sub}"
|
|
50
|
+
Rspec::Core::RakeTask.new do |t|
|
|
51
|
+
t.pattern = "./spec/#{sub}/**/*_spec.rb"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
#
|
|
56
|
+
# begin
|
|
57
|
+
# require 'spec/rake/spectask'
|
|
58
|
+
# rescue LoadError
|
|
59
|
+
# puts 'To use rspec for testing you must install rspec gem:'
|
|
60
|
+
# puts '$ sudo gem install rspec'
|
|
61
|
+
# exit
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# Spec::Rake::SpecTask.new(:spec) do |spec|
|
|
65
|
+
# spec.libs << 'lib' << 'spec'
|
|
66
|
+
# spec.spec_files = FileList['spec/**/*_spec.rb']
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
70
|
+
# spec.libs << 'lib' << 'spec'
|
|
71
|
+
# spec.pattern = 'spec/**/*_spec.rb'
|
|
72
|
+
# spec.rcov = true
|
|
73
|
+
# end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
task :default => :spec
|
|
77
|
+
|
|
78
|
+
require 'rake/rdoctask'
|
|
79
|
+
Rake::RDocTask.new do |rdoc|
|
|
80
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
|
81
|
+
|
|
82
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
83
|
+
rdoc.title = "very_nifty_generators #{version}"
|
|
84
|
+
rdoc.rdoc_files.include('README*')
|
|
85
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
86
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.0
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module ContentEngine
|
|
2
|
+
class ArticlesController < ContentEngineController
|
|
3
|
+
respond_to :html, :xml
|
|
4
|
+
|
|
5
|
+
# GET /articles
|
|
6
|
+
# GET /articles.xml
|
|
7
|
+
def index
|
|
8
|
+
@articles = @site.articles.order('created_at desc').all.paginate(:page => params[:page])
|
|
9
|
+
respond_with @articles
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# GET /articles/1
|
|
13
|
+
# GET /articles/1.xml
|
|
14
|
+
def show
|
|
15
|
+
respond_with @article = @site.articles.find(params[:id])
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# GET /articles/new
|
|
19
|
+
# GET /articles/new.xml
|
|
20
|
+
def new
|
|
21
|
+
respond_with @article = @site.articles.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# GET /articles/1/edit
|
|
25
|
+
def edit
|
|
26
|
+
respond_with @article = @site.articles.find(params[:id])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# POST /articles
|
|
30
|
+
# POST /articles.xml
|
|
31
|
+
def create
|
|
32
|
+
@article = @site.articles.new(params[:article])
|
|
33
|
+
flash[:notice] = "Article was successfully created." if @article.save
|
|
34
|
+
respond_with @article
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# PUT /articles/1
|
|
38
|
+
# PUT /articles/1.xml
|
|
39
|
+
def update
|
|
40
|
+
@article = @site.articles.find(params[:id])
|
|
41
|
+
flash[:notice] = "Article was successfully updated." if @article.update_attributes(params[:article])
|
|
42
|
+
respond_with @article
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# DELETE /articles/1
|
|
46
|
+
# DELETE /articles/1.xml
|
|
47
|
+
def destroy
|
|
48
|
+
@article = @site.articles.find(params[:id])
|
|
49
|
+
@article.destroy
|
|
50
|
+
|
|
51
|
+
respond_with @article
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module ContentEngine
|
|
2
|
+
class PagesController < ContentEngineController
|
|
3
|
+
respond_to :html, :xml
|
|
4
|
+
|
|
5
|
+
# GET /pages
|
|
6
|
+
# GET /pages.xml
|
|
7
|
+
def index
|
|
8
|
+
@pages = @site.pages.order('created_at desc').all.paginate(:page => params[:page])
|
|
9
|
+
@rootpage = @site.pages.root
|
|
10
|
+
|
|
11
|
+
if request.xhr? and params[:parent_id]
|
|
12
|
+
@rootpage = @site.pages.find(params[:parent_id])
|
|
13
|
+
return render(:partial => 'treeview')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
respond_with @pages
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# GET /pages/1
|
|
20
|
+
# GET /pages/1.xml
|
|
21
|
+
def show
|
|
22
|
+
respond_with @page = @site.pages.find(params[:id])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def lookup
|
|
26
|
+
@page = @site.pages.find_by_url(params[:url])
|
|
27
|
+
|
|
28
|
+
render :template => 'content_engine/pages/show'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# GET /pages/new
|
|
32
|
+
# GET /pages/new.xml
|
|
33
|
+
def new
|
|
34
|
+
respond_with @page = @site.pages.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# GET /pages/1/edit
|
|
38
|
+
def edit
|
|
39
|
+
respond_with @page = @site.pages.find(params[:id])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# POST /pages
|
|
43
|
+
# POST /pages.xml
|
|
44
|
+
def create
|
|
45
|
+
@page = @site.pages.new(params[:page])
|
|
46
|
+
flash[:notice] = "Page was successfully created." if @page.save
|
|
47
|
+
respond_with @page
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# PUT /pages/1
|
|
51
|
+
# PUT /pages/1.xml
|
|
52
|
+
def update
|
|
53
|
+
@page = @site.pages.find(params[:id])
|
|
54
|
+
flash[:notice] = "Page was successfully updated." if @page.update_attributes(params[:page])
|
|
55
|
+
respond_with @page
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# DELETE /pages/1
|
|
59
|
+
# DELETE /pages/1.xml
|
|
60
|
+
def destroy
|
|
61
|
+
@page = @site.pages.find(params[:id])
|
|
62
|
+
@page.destroy
|
|
63
|
+
|
|
64
|
+
respond_with @page
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# # GET /pages/1/move?to=(up|down)
|
|
69
|
+
# def move
|
|
70
|
+
# @page = @site.pages.find(params[:id])
|
|
71
|
+
# @page.move_left
|
|
72
|
+
#
|
|
73
|
+
# redirect_to pages_path
|
|
74
|
+
# end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class ContentEngineController < ApplicationController
|
|
2
|
+
protect_from_forgery
|
|
3
|
+
before_filter :load_site
|
|
4
|
+
|
|
5
|
+
protected
|
|
6
|
+
def load_site
|
|
7
|
+
# Single site mode
|
|
8
|
+
if Site.count == 1
|
|
9
|
+
@site = Site.first
|
|
10
|
+
else
|
|
11
|
+
@site = Site.find_by_host(request.host)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class Article < Content
|
|
2
|
+
has_permalink :title
|
|
3
|
+
acts_as_taggable_on :tags
|
|
4
|
+
|
|
5
|
+
validates_presence_of :title
|
|
6
|
+
validates_presence_of :body
|
|
7
|
+
|
|
8
|
+
def excerpt
|
|
9
|
+
content = body[0, 300]
|
|
10
|
+
words = content.gsub(/<\/?[^>]+>/, "").split(' ')
|
|
11
|
+
words[0 .. 59].join(' ') + '...'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def url
|
|
15
|
+
"/articles/%04d/%02d/%02d/#{permalink}" % [created_at.year, created_at.month, created_at.day]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def next
|
|
19
|
+
site.articles.published.order('id').where("id > ?", id).first
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def prev
|
|
23
|
+
site.articles.published.order('id').where("id < ?", id).first
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_param
|
|
27
|
+
"#{id}-#{permalink}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
class Content < ActiveRecord::Base
|
|
2
|
+
abstract_class = true
|
|
3
|
+
|
|
4
|
+
belongs_to :site
|
|
5
|
+
belongs_to :author, :class_name => 'User'
|
|
6
|
+
|
|
7
|
+
scope :recent, order("published_at DESC")
|
|
8
|
+
scope :published, lambda{
|
|
9
|
+
where("published_at is NOT null").where("published_at <= ?", Time.now).recent
|
|
10
|
+
}
|
|
11
|
+
scope :draft, where(:published_at => nil).recent
|
|
12
|
+
|
|
13
|
+
before_save :update_published_at
|
|
14
|
+
|
|
15
|
+
def body
|
|
16
|
+
self[:body]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def body_html
|
|
20
|
+
if self.body_source
|
|
21
|
+
markdown = RDiscount.new(self.body_source)
|
|
22
|
+
@body_html ||= markdown.to_html
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
alias_method :body_source, :body
|
|
26
|
+
alias_method :body, :body_html
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def author_name
|
|
30
|
+
author.display_name if author
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def status
|
|
34
|
+
published? ? 'published' : 'draft'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def status=(status)
|
|
38
|
+
if status == 'draft'
|
|
39
|
+
self[:published_at] = nil
|
|
40
|
+
else
|
|
41
|
+
self[:published_at] = Time.now unless published?
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def published=(flag)
|
|
46
|
+
@mark_published = flag
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def published?
|
|
50
|
+
published_at != nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def publish!
|
|
54
|
+
self.published = true
|
|
55
|
+
self.send(:update_published_at)
|
|
56
|
+
save
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
def update_published_at
|
|
61
|
+
self[:published_at] = Time.now if @mark_published and not published?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
data/app/models/page.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
class Page < Content
|
|
2
|
+
belongs_to :parent, :class_name => 'Page'
|
|
3
|
+
has_many :children, :class_name => 'Page', :foreign_key => 'parent_id'
|
|
4
|
+
|
|
5
|
+
acts_as_taggable_on :tags
|
|
6
|
+
|
|
7
|
+
before_validation :ensure_hierarchy
|
|
8
|
+
has_permalink :title, :scope => :parent_id, :unless => :homepage?
|
|
9
|
+
|
|
10
|
+
validates_presence_of :title, :body
|
|
11
|
+
|
|
12
|
+
# Tree structure
|
|
13
|
+
scope :roots, where(:parent_id => nil)
|
|
14
|
+
|
|
15
|
+
def self.root
|
|
16
|
+
roots.first
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def root?
|
|
20
|
+
self == Page.root || Page.count == 0 && new_record?
|
|
21
|
+
end
|
|
22
|
+
alias :homepage? :root?
|
|
23
|
+
|
|
24
|
+
def ancestors
|
|
25
|
+
node, nodes = self, []
|
|
26
|
+
nodes << node = node.parent while node.parent
|
|
27
|
+
nodes
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def url
|
|
31
|
+
links = (ancestors.map(&:permalink) << permalink).delete_if(&:blank?)
|
|
32
|
+
|
|
33
|
+
"/#{links.join('/')}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
def ensure_hierarchy
|
|
38
|
+
self.parent = site.homepage if !parent_id && site && site.homepage
|
|
39
|
+
end
|
|
40
|
+
end
|
data/app/models/site.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class Site < ActiveRecord::Base
|
|
2
|
+
belongs_to :owner, :class_name => 'User'
|
|
3
|
+
|
|
4
|
+
has_many :memberships, :dependent => :destroy
|
|
5
|
+
has_many :users, :through => :memberships
|
|
6
|
+
|
|
7
|
+
# has_many :tags, :dependent => :destroy
|
|
8
|
+
has_many :articles, :dependent => :destroy
|
|
9
|
+
has_many :pages, :dependent => :destroy do
|
|
10
|
+
def find_by_url(url = [])
|
|
11
|
+
url = url.split('/').delete_if(&:blank?) if url.is_a? String
|
|
12
|
+
|
|
13
|
+
page = where(:parent_id => nil).first
|
|
14
|
+
|
|
15
|
+
url.each do |part|
|
|
16
|
+
page = page.children.published.where(:permalink => part).first
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
page
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def homepage
|
|
24
|
+
pages.root
|
|
25
|
+
end
|
|
26
|
+
end
|
data/app/models/tag.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
class Tag < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
attr_accessible :name
|
|
4
|
+
|
|
5
|
+
### ASSOCIATIONS:
|
|
6
|
+
|
|
7
|
+
has_many :taggings, :dependent => :destroy
|
|
8
|
+
|
|
9
|
+
### VALIDATIONS:
|
|
10
|
+
|
|
11
|
+
validates_presence_of :name
|
|
12
|
+
validates_uniqueness_of :name
|
|
13
|
+
|
|
14
|
+
### NAMED SCOPES:
|
|
15
|
+
|
|
16
|
+
scope :named, lambda { |name| { :conditions => ["name LIKE ?", name] } }
|
|
17
|
+
scope :named_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", tag.to_s]) }.join(" OR ") } }
|
|
18
|
+
scope :named_like, lambda { |name| { :conditions => ["name LIKE ?", "%#{name}%"] } }
|
|
19
|
+
scope :named_like_any, lambda { |list| { :conditions => list.map { |tag| sanitize_sql(["name LIKE ?", "%#{tag.to_s}%"]) }.join(" OR ") } }
|
|
20
|
+
|
|
21
|
+
### CLASS METHODS:
|
|
22
|
+
|
|
23
|
+
def self.find_or_create_with_like_by_name(name)
|
|
24
|
+
named_like(name).first || create(:name => name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.find_or_create_all_with_like_by_name(*list)
|
|
28
|
+
list = [list].flatten
|
|
29
|
+
|
|
30
|
+
return [] if list.empty?
|
|
31
|
+
|
|
32
|
+
existing_tags = Tag.named_any(list).all
|
|
33
|
+
new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.downcase == name.downcase } }
|
|
34
|
+
created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
|
|
35
|
+
|
|
36
|
+
existing_tags + created_tags
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
### INSTANCE METHODS:
|
|
40
|
+
|
|
41
|
+
def ==(object)
|
|
42
|
+
super || (object.is_a?(Tag) && name == object.name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_s
|
|
46
|
+
name
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def count
|
|
50
|
+
read_attribute(:count).to_i
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
data/app/models/user.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
3
|
+
<html>
|
|
4
|
+
<head>
|
|
5
|
+
<title><%= @site.title %> - Content Engine</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<div id="container">
|
|
9
|
+
<%- flash.each do |name, msg| -%>
|
|
10
|
+
<%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
|
|
11
|
+
<%- end -%>
|
|
12
|
+
|
|
13
|
+
<%= yield %>
|
|
14
|
+
</div>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require File.expand_path("../../content_engine", __FILE__)
|