qcms 1.3.3

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.
Files changed (53) hide show
  1. data/.gitignore +14 -0
  2. data/README +70 -0
  3. data/Rakefile +39 -0
  4. data/VERSION +1 -0
  5. data/app/controllers/admin/admin_controller.rb +12 -0
  6. data/app/controllers/admin/documents_controller.rb +133 -0
  7. data/app/controllers/admin/meta_definitions_controller.rb +36 -0
  8. data/app/controllers/documents_controller.rb +187 -0
  9. data/app/controllers/sendmail_controller.rb +87 -0
  10. data/app/helpers/admin/documents_helper.rb +71 -0
  11. data/app/helpers/documents_helper.rb +30 -0
  12. data/app/helpers/sendmail_helper.rb +2 -0
  13. data/app/models/document.rb +292 -0
  14. data/app/models/document_mailer.rb +13 -0
  15. data/app/models/document_sweeper.rb +28 -0
  16. data/app/models/meta_definition.rb +57 -0
  17. data/app/models/sendmail.rb +18 -0
  18. data/app/views/admin/admin/system.html.erb +34 -0
  19. data/app/views/admin/documents/_form.html.erb +60 -0
  20. data/app/views/admin/documents/default.edit.html.erb +10 -0
  21. data/app/views/admin/documents/default.new.html.erb +10 -0
  22. data/app/views/admin/documents/default.show.html.erb +77 -0
  23. data/app/views/admin/documents/index.html.erb +32 -0
  24. data/app/views/admin/documents/shared/_resource_link.html.erb +6 -0
  25. data/app/views/document_mailer/new_document.erb +12 -0
  26. data/app/views/layouts/admin.html.erb +43 -0
  27. data/app/views/layouts/application.html.erb +44 -0
  28. data/app/views/pages/404.html.erb +28 -0
  29. data/app/views/pages/contact.html.erb +42 -0
  30. data/app/views/pages/default.html.erb +12 -0
  31. data/app/views/pages/feed.rss.builder +17 -0
  32. data/app/views/pages/home.html.erb +5 -0
  33. data/app/views/pages/search.html.erb +19 -0
  34. data/app/views/pages/shared/_archived_pages.erb +16 -0
  35. data/app/views/pages/shared/_related_pages.html.erb +13 -0
  36. data/app/views/pages/sitemap.html.erb +9 -0
  37. data/app/views/pages/template.erb +51 -0
  38. data/app/views/pages/thank_you.html.erb +1 -0
  39. data/app/views/sendmail/default.erb +12 -0
  40. data/config/sitemap.example.yml +39 -0
  41. data/db/migrate/20090824150210_create_documents.rb +37 -0
  42. data/db/migrate/20091208124512_create_meta_definition.rb +26 -0
  43. data/init.rb +1 -0
  44. data/install.rb +1 -0
  45. data/lib/qcms.rb +4 -0
  46. data/lib/tasks/cms.rake +237 -0
  47. data/qcms.gemspec +91 -0
  48. data/rails/init.rb +3 -0
  49. data/tasks/qcms_tasks.rake +223 -0
  50. data/test/qwerty_test.rb +8 -0
  51. data/test/test_helper.rb +3 -0
  52. data/uninstall.rb +1 -0
  53. metadata +107 -0
@@ -0,0 +1,14 @@
1
+ *.log
2
+ db/schema.rb
3
+ db/schema.sql
4
+ .DS_Store
5
+ doc/api
6
+ doc/app
7
+ config/database.yml
8
+ config/settings.yml
9
+ config/authorisation.yml
10
+ nbproject
11
+ pkg/**
12
+ .yardoc/*
13
+ doc/*
14
+ rdoc/*
data/README ADDED
@@ -0,0 +1,70 @@
1
+ Qwerty CMS
2
+ ==========
3
+
4
+ A unique CMS provided as a Rails 2 engine
5
+
6
+ Key Features
7
+
8
+ * Extended template pathing
9
+ ** blog.post.erb.html
10
+ ** blog.post.comment.erb.html
11
+ ** homepage.erb.html
12
+ ** gallery.picture.erb.html
13
+
14
+ * sitemap.yml
15
+ ** A nice way to represent a sites nested structure and defaults for each level such as pagination and ordering
16
+
17
+ * Not bound to a autherisation or authentication system
18
+
19
+ == Install
20
+
21
+ sudo gem install qcms
22
+
23
+ == Get going
24
+
25
+ Firstly, it is easiest to start using Skeletor app which provides a bare bones application with examples configuration files, and Authlogic integration.
26
+
27
+ Otherwise:
28
+
29
+ Copy the migrations and rake tasks to your Rails app.
30
+
31
+ config.gem "qcms", :version => '~>1.0'
32
+
33
+ Create sitemap.yml (see skeletor for examples)
34
+
35
+ rake qwerty:bootstrap
36
+
37
+ == TODO
38
+
39
+ * migrations to work from inside a gem
40
+ * rake tasks to work from inside a gem.
41
+ * Specify gem dependancies
42
+
43
+ == Release Gem with Jeweler and Git to RubyGems
44
+
45
+ (next) git co -b release_1.2.1
46
+ (release_1.2.1) rake version:bump:patch
47
+ (release_1.2.1) git co master
48
+ (master) git merge --no-ff release_1.2.1
49
+ (master) rake release
50
+ (master) git co next
51
+ (next) git merge --no-ff release_1.2.1
52
+
53
+ == Versioning
54
+
55
+ Versioning schema: http://semver.org/
56
+
57
+ == Upgrade Notes
58
+
59
+ = 1.3
60
+
61
+ settings.yml
62
+
63
+ documents
64
+ cache: true
65
+
66
+ == Authors
67
+
68
+ Kris Leech @ Interkonect Limited
69
+
70
+ http://interkonect.com
@@ -0,0 +1,39 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the qwerty plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the qwerty plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'Qwerty'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ rdoc.rdoc_files.include('app/**/*.rb')
24
+ end
25
+
26
+ begin
27
+ require 'jeweler'
28
+ Jeweler::Tasks.new do |gemspec|
29
+ gemspec.name = "qcms"
30
+ gemspec.summary = "A CMS built in collaberation with designers"
31
+ gemspec.description = "Key CMS features: extended template pathing, sitemap.yml, simple configurable, deeply nestable content"
32
+ gemspec.email = "kris.leech@interkonect.com"
33
+ gemspec.homepage = "http://github.com/krisleech/qcms"
34
+ gemspec.authors = ["Kris Leech"]
35
+ end
36
+ Jeweler::GemcutterTasks.new
37
+ rescue LoadError
38
+ puts "Jeweler not available. Install it with: gem install jeweler"
39
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.3.3
@@ -0,0 +1,12 @@
1
+ class Admin::AdminController < ApplicationController
2
+ before_filter :require_user
3
+
4
+ layout 'admin'
5
+
6
+ def system
7
+ end
8
+
9
+ def dashboard
10
+ redirect_to admin_documents_path
11
+ end
12
+ end
@@ -0,0 +1,133 @@
1
+ class Admin::DocumentsController < Admin::AdminController
2
+ before_filter :find_document, :except => [:index]
3
+ before_filter :catch_sti_problem, :only => [:update]
4
+
5
+ cache_sweeper :document_sweeper, :only => [:create, :update, :destroy]
6
+
7
+ def index
8
+ @documents = Document.roots.sort_by(&:position)
9
+ end
10
+
11
+ def show
12
+ render :text => "Document Not Found [#{params[:id]}]" and return unless @document
13
+ render :template => admin_view_for('show')
14
+ end
15
+
16
+ def new
17
+ @document.type = params[:type] || 'Document'
18
+ if params[:parent]
19
+ @document.parent = Document.find(params[:parent])
20
+ @document.meta_definition = @document.parent.meta_definition.children.by_label(params[:label]).first
21
+ else
22
+ @document.meta_definition = MetaDefinition.find_by_label_path(params[:label_path])
23
+ end
24
+ @document.label = params[:label] || @document.meta_definition.label
25
+ render :template => admin_view_for('new')
26
+ end
27
+
28
+ def create
29
+ @document = Document.create(params[:document])
30
+ @document.type = params[:document][:type] || 'Document'
31
+ @document.author = current_user
32
+ if @document.save
33
+ flash[:notice] = "Succesfully Saved"
34
+ redirect_to parent_path
35
+ else
36
+ render :template => admin_view_for('new')
37
+ end
38
+ end
39
+
40
+ def edit
41
+ render :template => admin_view_for('edit')
42
+ end
43
+
44
+ def update
45
+ if @document.update_attributes(params[:document])
46
+ flash[:notice] = "Succesfully Saved"
47
+ redirect_to parent_path
48
+ else
49
+ render :template => admin_view_for('edit')
50
+ end
51
+ end
52
+
53
+ def destroy
54
+ @document.destroy
55
+ redirect_to parent_path
56
+ end
57
+
58
+ def up
59
+ @document.move_higher
60
+ redirect_to parent_path
61
+ end
62
+
63
+ def down
64
+ @document.move_lower
65
+ redirect_to parent_path
66
+ end
67
+
68
+ # Generate a view from template.erb
69
+ def generate_template
70
+ if request.post?
71
+ template = File.new("#{RAILS_ROOT}/app/views/pages/#{@document.meta_definition.label_path.gsub('/', '.')}.html.erb", 'w')
72
+ template.write(ERB.new(IO.read("#{RAILS_ROOT}/app/views/pages/template.erb")).result(binding))
73
+ template.close
74
+ end
75
+ @document.touch # delete cache (FIXME: Not ideal)
76
+ redirect_to document_path(@document)
77
+ end
78
+
79
+ private
80
+
81
+ def find_document
82
+ if params[:id]
83
+ @document = Document.find(params[:id])
84
+ else
85
+ @document = Document.new
86
+ end
87
+ end
88
+
89
+ def parent_path
90
+ if @document.parent
91
+ admin_document_path(@document.parent.id)
92
+ else
93
+ admin_dashboard_path
94
+ end
95
+ end
96
+
97
+
98
+ # eg. my_first_post.html.erb, blog.post.comment.html.erb, default.html.erb
99
+ def admin_view_for(action, options = {})
100
+ options[:extension] = '.html.erb'
101
+
102
+ filenames = [@document.permalink, @document.meta_definition.label_path.gsub('/', '.'), 'default']
103
+
104
+ view_paths.each do | view_path |
105
+ filenames.each do | filename |
106
+ next if filename.nil?
107
+ target = File.join('admin', 'documents', filename + '.' + action) + options[:extension]
108
+ target_with_view_path = File.join(view_path, target)
109
+ if File.exists? target_with_view_path
110
+ headers['content-type'] = 'text/html' # Otherwise wrong content-type is set due to multiple file extensions
111
+ logger.debug 'TEMPLATE FOUND: ' + target_with_view_path
112
+ return target
113
+ else
114
+ logger.debug 'TEMPLATE NOT FOR: ' + target_with_view_path
115
+ end
116
+ end
117
+ end
118
+ raise 'No Template found'
119
+ end
120
+
121
+
122
+ # No idea why this happens but if the inputs are (incorrectly) named page[field] instead of document[field] you get
123
+ # params[:document] from now where. This would be okay but sometimes fields appear to be missing when using fckeditor(!?!)
124
+ # For the new form @document is_a? Document (not yet 'typed' via STI), but when editing it is_a? Page or Folder...
125
+ # To get the correct input names @document needs to be type cast to Document class
126
+ # Example: form_for @document.becomes(Document)
127
+ def catch_sti_problem
128
+ return unless RAILS_ENV == 'development'
129
+ if params[:document] && params[:page]
130
+ raise 'Both params[:document] and params[:page] found, you might need to add @document.becomes(Document) to get the correct input names'
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,36 @@
1
+ class Admin::MetaDefinitionsController < Admin::AdminController
2
+
3
+ def index
4
+ end
5
+
6
+ def show
7
+ end
8
+
9
+ def new
10
+ end
11
+
12
+ def create
13
+ end
14
+
15
+ def edit
16
+ end
17
+
18
+ def update
19
+ @meta_definition = MetaDefinition.find(params[:id])
20
+ if @meta_definition.update_attributes(params[:meta_definition])
21
+ flash[:notice] = "Succesfully Saved"
22
+ else
23
+ flash[:notice] = 'Not saved'
24
+ end
25
+ redirect_to admin_document_path(params[:document_id])
26
+ end
27
+
28
+
29
+
30
+
31
+
32
+
33
+ private
34
+
35
+
36
+ end
@@ -0,0 +1,187 @@
1
+ class DocumentsController < ApplicationController
2
+ unloadable
3
+ skip_before_filter :require_user
4
+
5
+ # Show a document:
6
+ # Route using catch-all, /*path, or, pass specific 'id', :path => 'gallery'
7
+ def show
8
+ render :file => cache_file and return if cached_file?
9
+
10
+ @document = Document.find_by_path(request_path)
11
+
12
+ raise ActiveRecord::RecordNotFound unless @document and (@document.published? or @document.allowed? current_user, 'read')
13
+
14
+ respond_to do |format|
15
+ format.html do
16
+ setup_view_environment
17
+ render :template => view_for
18
+ end
19
+ format.xml { render :xml => @document.to_xml(:only => [:title, :summary, :body, :published_at, :permalink]) }
20
+ format.rss { render :template => 'pages/feed.rss.builder' }
21
+ end
22
+
23
+ cache_this_page!
24
+ end
25
+
26
+ # Show all pages in any Document for a particular month/year
27
+ # /*path/archive/:month/:year
28
+ def archive
29
+ render :file => cache_file and return if cached_file?
30
+ @document = Document.public.find_by_path(params[:path].join('/'))
31
+ raise ActiveRecord::RecordNotFound if @document.nil?
32
+
33
+
34
+ @documents = @document.archive_for(params[:month], params[:year]).paginate :page => params[:page], :per_page => Settings.documents.per_page
35
+ render :template => view_for(:suffix => '_archive')
36
+ cache_this_page!
37
+ end
38
+
39
+ # Allows searching of pages in a folder
40
+ # /:permalink/search?search[title_like]=hello
41
+ def search
42
+ params[:search] ||= {}
43
+ @document = Document.public.find_by_path(params[:path].join('/'))
44
+ params[:search].merge!(:parent_id => @document.id)
45
+ params[:search][:state_eq] = 'published'
46
+ @documents = Document.search(params[:search]).paginate :page => params[:page], :per_page => Settings.documents.per_page
47
+ setup_view_environment
48
+ render :template => view_for(:suffix => '_search')
49
+ end
50
+
51
+ # Allow Document wide search
52
+ # /search?terms[title_like]=hello
53
+ def search_all
54
+ params[:search] ||= {}
55
+ params[:search][:state_eq] = 'published'
56
+ @documents = Document.search(params[:search]).paginate :page => params[:page], :per_page => Settings.documents.per_page
57
+ render :template => '/pages/search'
58
+ end
59
+
60
+ # public facing creation of documents
61
+ def create
62
+ @document = Document.public.find(params[:id])
63
+
64
+ begin
65
+ do_human_test
66
+ rescue
67
+ flash.now[:notice] = 'You did not add up the numbers correctly, please try again.'
68
+
69
+ new_document = Document.new
70
+ new_document.body = params[:document][:body]
71
+
72
+ eval("@new_#{params[:label]} = new_document")
73
+
74
+ setup_view_environment
75
+ render :template => view_for
76
+ return
77
+ end
78
+
79
+ params[:document][:state] = nil # prevent auto approval hack (FIXME: use attr_protected)
80
+
81
+
82
+ if @document.meta_definition_for(params[:label]).allowed? current_user, 'create'
83
+
84
+ new_document = Document.new(params[:document])
85
+ new_document.parent = @document
86
+ new_document.label = params[:label]
87
+ new_document.author = current_user || User.anonymous
88
+ new_document.published_at = Time.now
89
+
90
+ if new_document.save
91
+ flash[:notice] = new_document.meta_definition.flash_messages['create'] || "Your #{params[:label]} has been saved"
92
+ if new_document.meta_definition.notify_admins
93
+ DocumentMailer.deliver_new_document(new_document)
94
+ end
95
+ redirect_to document_path(@document)
96
+ else
97
+ flash.now[:notice] = 'Could not be saved'
98
+ eval("@new_#{params[:label]} = new_document")
99
+ setup_view_environment
100
+ render :template => view_for
101
+ end
102
+ else
103
+ render :text => 'Not Allowed' and return
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ def request_path
110
+ @path ||= CGI::unescape(request.path[1..-1])
111
+ @path.blank? ? 'home' : @path
112
+ end
113
+
114
+ # Does a cache exist for this URL
115
+ def cached_file?
116
+ Settings.documents.page_cache && File.exists?(cache_file)
117
+ end
118
+
119
+ def cache_file
120
+ @url_id ||= request_path.split('/').last + (request.query_string.blank? ? '' : '_' + Digest::MD5.hexdigest(request.query_string))
121
+ @cache_file ||= File.join(RAILS_ROOT, 'public', 'cache', request_path, self.action_name, @url_id) + '.html'
122
+ end
123
+
124
+ def cache_this_page!
125
+ if Settings.documents.page_cache && !File.exists?(cache_file)
126
+ logger.debug 'Caching Page: ' + cache_file
127
+ FileUtils.mkdir_p File.dirname(cache_file)
128
+ cache = File.open(cache_file, 'w')
129
+ cache.write(response.body)
130
+ cache.close
131
+ end
132
+ end
133
+
134
+ def do_human_test
135
+ raise 'Human Test Failed' unless params[:human_test][:answer].crypt('humAn5') == params[:human_test][:crypted_answer]
136
+ end
137
+
138
+ def setup_view_environment
139
+ # create children vars such as @comments
140
+ if @document.can_have_children?
141
+
142
+ @children = []
143
+
144
+ # make an instance variable for each child eg. @posts @comments etc.
145
+ @document.child_labels.each do | label|
146
+ per_page = @document.meta_definition.children.by_label(label).first.per_page || Settings.documents.per_page
147
+ order = @document.meta_definition.children.by_label(label).first.sort_by || Settings.documents._sort_by
148
+ instance_variable_set('@' + label.pluralize, @document.children.by_label(label).with_states(:published).paginate(:order => order, :page => params[:page], :per_page => per_page))
149
+ end
150
+
151
+ end
152
+
153
+ # make instance variable for the parent eg. @story
154
+ instance_variable_set("@#{@document.label.gsub(' ', '_')}", @document)
155
+
156
+ # HTML meta tags
157
+ @page_title = @document.meta_title
158
+ @meta_description = @document.meta_description
159
+ @meta_keywords = @document.meta_keywords
160
+ end
161
+
162
+
163
+ # eg. blog.post.comment.html.erb
164
+ def view_for(options = {})
165
+ create_missing_template if !params[:create_template].nil? && RAILS_ENV == 'development'
166
+
167
+ options[:extension] = '.html.erb'
168
+ options[:prefix] ||= ''
169
+ options[:suffix] ||= ''
170
+
171
+ filenames = [@document.permalink, @document.meta_definition.template_filename(false), 'default']
172
+
173
+ view_paths.each do | view_path |
174
+ filenames.each do | filename |
175
+ target = File.join('pages', filename) + options[:suffix] + options[:extension]
176
+ target_with_view_path = File.join(view_path, target)
177
+ if File.exists? target_with_view_path
178
+ logger.debug 'FOUND: ' + target_with_view_path
179
+ return target
180
+ else
181
+ logger.debug 'NOT FOUND: ' + target_with_view_path
182
+ end
183
+ end
184
+ end
185
+ raise 'No Template found'
186
+ end
187
+ end