monologue 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.
Files changed (63) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +73 -0
  3. data/Rakefile +32 -0
  4. data/app/assets/javascripts/monologue/admin/application.js +13 -0
  5. data/app/assets/javascripts/monologue/admin/tinymce-config.js +21 -0
  6. data/app/assets/javascripts/monologue/blog/application.js +9 -0
  7. data/app/assets/stylesheets/monologue/admin/application.css +10 -0
  8. data/app/assets/stylesheets/monologue/blog/application.css +13 -0
  9. data/app/assets/stylesheets/monologue/blog/custom.css +1 -0
  10. data/app/assets/stylesheets/monologue/blog/fonts.css +1 -0
  11. data/app/assets/stylesheets/monologue/blog/monologue.css +37 -0
  12. data/app/assets/stylesheets/monologue/blog/skeleton/base.css +343 -0
  13. data/app/assets/stylesheets/monologue/blog/skeleton/layout.css +58 -0
  14. data/app/assets/stylesheets/monologue/blog/skeleton/skeleton.css +242 -0
  15. data/app/controllers/monologue/admin/base_controller.rb +12 -0
  16. data/app/controllers/monologue/admin/posts_controller.rb +54 -0
  17. data/app/controllers/monologue/admin/sessions_controller.rb +22 -0
  18. data/app/controllers/monologue/application_controller.rb +22 -0
  19. data/app/controllers/monologue/posts_controller.rb +25 -0
  20. data/app/form_builders/monologue_admin_form_builder.rb +61 -0
  21. data/app/helpers/monologue/admin/admin_helper.rb +4 -0
  22. data/app/helpers/monologue/application_helper.rb +8 -0
  23. data/app/helpers/monologue/posts_helper.rb +4 -0
  24. data/app/helpers/monologue/sessions_helper.rb +6 -0
  25. data/app/models/monologue/post.rb +40 -0
  26. data/app/models/monologue/posts_revision.rb +34 -0
  27. data/app/models/monologue/user.rb +6 -0
  28. data/app/sweepers/monologue/posts_sweeper.rb +28 -0
  29. data/app/views/layouts/monologue/_google_analytics.html.erb +15 -0
  30. data/app/views/layouts/monologue/admin.html.erb +21 -0
  31. data/app/views/layouts/monologue/admin/_nav_bar.html.erb +24 -0
  32. data/app/views/layouts/monologue/application.html.erb +60 -0
  33. data/app/views/monologue/admin/posts/_form.html.erb +16 -0
  34. data/app/views/monologue/admin/posts/edit.html.erb +4 -0
  35. data/app/views/monologue/admin/posts/index.html.erb +20 -0
  36. data/app/views/monologue/admin/posts/new.html.erb +4 -0
  37. data/app/views/monologue/admin/sessions/new.html.erb +13 -0
  38. data/app/views/monologue/posts/404.html.erb +9 -0
  39. data/app/views/monologue/posts/_pagination.html.erb +9 -0
  40. data/app/views/monologue/posts/_social_sharing.html.erb +42 -0
  41. data/app/views/monologue/posts/feed.rss.builder +19 -0
  42. data/app/views/monologue/posts/index.html.erb +18 -0
  43. data/app/views/monologue/posts/show.html.erb +38 -0
  44. data/config/locales/en.yml +106 -0
  45. data/config/locales/fr.yml +106 -0
  46. data/config/routes.rb +15 -0
  47. data/db/migrate/20120114001001_create_monologue_users.rb +11 -0
  48. data/db/migrate/20120120193858_create_monologue_posts_revisions.rb +18 -0
  49. data/db/migrate/20120120193907_create_monologue_posts.rb +10 -0
  50. data/db/seeds.rb +1 -0
  51. data/lib/monologue.rb +17 -0
  52. data/lib/monologue/engine.rb +11 -0
  53. data/lib/monologue/version.rb +3 -0
  54. data/lib/tasks/monologue_tasks.rake +4 -0
  55. data/vendor/assets/images/monologue/bootstrap/glyphicons-halflings-white.png +0 -0
  56. data/vendor/assets/images/monologue/bootstrap/glyphicons-halflings.png +0 -0
  57. data/vendor/assets/javascripts/monologue/bootstrap/bootstrap-datepicker-fr.js +9 -0
  58. data/vendor/assets/javascripts/monologue/bootstrap/bootstrap-datepicker.js +338 -0
  59. data/vendor/assets/javascripts/monologue/bootstrap/bootstrap.min.js +1 -0
  60. data/vendor/assets/stylesheets/monologue/bootstrap/bootstrap-datepicker.css +130 -0
  61. data/vendor/assets/stylesheets/monologue/bootstrap/bootstrap-responsive.min.css +3 -0
  62. data/vendor/assets/stylesheets/monologue/bootstrap/bootstrap.min.css +610 -0
  63. metadata +285 -0
@@ -0,0 +1,58 @@
1
+ /*
2
+ * Skeleton V1.1
3
+ * Copyright 2011, Dave Gamache
4
+ * www.getskeleton.com
5
+ * Free to use under the MIT license.
6
+ * http://www.opensource.org/licenses/mit-license.php
7
+ * 8/17/2011
8
+ */
9
+
10
+ /* Table of Content
11
+ ==================================================
12
+ #Site Styles
13
+ #Page Styles
14
+ #Media Queries
15
+ #Font-Face */
16
+
17
+ /* #Site Styles
18
+ ================================================== */
19
+
20
+ /* #Page Styles
21
+ ================================================== */
22
+
23
+ /* #Media Queries
24
+ ================================================== */
25
+
26
+ /* Smaller than standard 960 (devices and browsers) */
27
+ @media only screen and (max-width: 959px) {}
28
+
29
+ /* Tablet Portrait size to standard 960 (devices and browsers) */
30
+ @media only screen and (min-width: 768px) and (max-width: 959px) {}
31
+
32
+ /* All Mobile Sizes (devices and browser) */
33
+ @media only screen and (max-width: 767px) {}
34
+
35
+ /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */
36
+ @media only screen and (min-width: 480px) and (max-width: 767px) {}
37
+
38
+ /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */
39
+ @media only screen and (max-width: 479px) {}
40
+
41
+
42
+ /* #Font-Face
43
+ ================================================== */
44
+ /* This is the proper syntax for an @font-face file
45
+ Just create a "fonts" folder at the root,
46
+ copy your FontName into code below and remove
47
+ comment brackets */
48
+
49
+ /* @font-face {
50
+ font-family: 'FontName';
51
+ src: url('../fonts/FontName.eot');
52
+ src: url('../fonts/FontName.eot?iefix') format('eot'),
53
+ url('../fonts/FontName.woff') format('woff'),
54
+ url('../fonts/FontName.ttf') format('truetype'),
55
+ url('../fonts/FontName.svg#webfontZam02nTh') format('svg');
56
+ font-weight: normal;
57
+ font-style: normal; }
58
+ */
@@ -0,0 +1,242 @@
1
+ /*
2
+ * Skeleton V1.1
3
+ * Copyright 2011, Dave Gamache
4
+ * www.getskeleton.com
5
+ * Free to use under the MIT license.
6
+ * http://www.opensource.org/licenses/mit-license.php
7
+ * 8/17/2011
8
+ */
9
+
10
+
11
+ /* Table of Contents
12
+ ==================================================
13
+ #Base 960 Grid
14
+ #Tablet (Portrait)
15
+ #Mobile (Portrait)
16
+ #Mobile (Landscape)
17
+ #Clearing */
18
+
19
+
20
+
21
+ /* #Base 960 Grid
22
+ ================================================== */
23
+
24
+ .container { position: relative; width: 960px; margin: 0 auto; padding: 0; }
25
+ .container .column,
26
+ .container .columns { float: left; display: inline; margin-left: 10px; margin-right: 10px; }
27
+ .row { margin-bottom: 20px; }
28
+
29
+ /* Nested Column Classes */
30
+ .column.alpha, .columns.alpha { margin-left: 0; }
31
+ .column.omega, .columns.omega { margin-right: 0; }
32
+
33
+ /* Base Grid */
34
+ .container .one.column,
35
+ .container .one.columns { width: 40px; }
36
+ .container .two.columns { width: 100px; }
37
+ .container .three.columns { width: 160px; }
38
+ .container .four.columns { width: 220px; }
39
+ .container .five.columns { width: 280px; }
40
+ .container .six.columns { width: 340px; }
41
+ .container .seven.columns { width: 400px; }
42
+ .container .eight.columns { width: 460px; }
43
+ .container .nine.columns { width: 520px; }
44
+ .container .ten.columns { width: 580px; }
45
+ .container .eleven.columns { width: 640px; }
46
+ .container .twelve.columns { width: 700px; }
47
+ .container .thirteen.columns { width: 760px; }
48
+ .container .fourteen.columns { width: 820px; }
49
+ .container .fifteen.columns { width: 880px; }
50
+ .container .sixteen.columns { width: 940px; }
51
+
52
+ .container .one-third.column { width: 300px; }
53
+ .container .two-thirds.column { width: 620px; }
54
+
55
+ /* Offsets */
56
+ .container .offset-by-one { padding-left: 60px; }
57
+ .container .offset-by-two { padding-left: 120px; }
58
+ .container .offset-by-three { padding-left: 180px; }
59
+ .container .offset-by-four { padding-left: 240px; }
60
+ .container .offset-by-five { padding-left: 300px; }
61
+ .container .offset-by-six { padding-left: 360px; }
62
+ .container .offset-by-seven { padding-left: 420px; }
63
+ .container .offset-by-eight { padding-left: 480px; }
64
+ .container .offset-by-nine { padding-left: 540px; }
65
+ .container .offset-by-ten { padding-left: 600px; }
66
+ .container .offset-by-eleven { padding-left: 660px; }
67
+ .container .offset-by-twelve { padding-left: 720px; }
68
+ .container .offset-by-thirteen { padding-left: 780px; }
69
+ .container .offset-by-fourteen { padding-left: 840px; }
70
+ .container .offset-by-fifteen { padding-left: 900px; }
71
+
72
+
73
+
74
+ /* #Tablet (Portrait)
75
+ ================================================== */
76
+
77
+ /* Note: Design for a width of 768px */
78
+
79
+ @media only screen and (min-width: 768px) and (max-width: 959px) {
80
+ .container { width: 768px; }
81
+ .container .column,
82
+ .container .columns { margin-left: 10px; margin-right: 10px; }
83
+ .column.alpha, .columns.alpha { margin-left: 0; margin-right: 10px; }
84
+ .column.omega, .columns.omega { margin-right: 0; margin-left: 10px; }
85
+ .alpha.omega { margin-left: 0; margin-right: 0; }
86
+
87
+ .container .one.column,
88
+ .container .one.columns { width: 28px; }
89
+ .container .two.columns { width: 76px; }
90
+ .container .three.columns { width: 124px; }
91
+ .container .four.columns { width: 172px; }
92
+ .container .five.columns { width: 220px; }
93
+ .container .six.columns { width: 268px; }
94
+ .container .seven.columns { width: 316px; }
95
+ .container .eight.columns { width: 364px; }
96
+ .container .nine.columns { width: 412px; }
97
+ .container .ten.columns { width: 460px; }
98
+ .container .eleven.columns { width: 508px; }
99
+ .container .twelve.columns { width: 556px; }
100
+ .container .thirteen.columns { width: 604px; }
101
+ .container .fourteen.columns { width: 652px; }
102
+ .container .fifteen.columns { width: 700px; }
103
+ .container .sixteen.columns { width: 748px; }
104
+
105
+ .container .one-third.column { width: 236px; }
106
+ .container .two-thirds.column { width: 492px; }
107
+
108
+ /* Offsets */
109
+ .container .offset-by-one { padding-left: 48px; }
110
+ .container .offset-by-two { padding-left: 96px; }
111
+ .container .offset-by-three { padding-left: 144px; }
112
+ .container .offset-by-four { padding-left: 192px; }
113
+ .container .offset-by-five { padding-left: 240px; }
114
+ .container .offset-by-six { padding-left: 288px; }
115
+ .container .offset-by-seven { padding-left: 336px; }
116
+ .container .offset-by-eight { padding-left: 384px; }
117
+ .container .offset-by-nine { padding-left: 432px; }
118
+ .container .offset-by-ten { padding-left: 480px; }
119
+ .container .offset-by-eleven { padding-left: 528px; }
120
+ .container .offset-by-twelve { padding-left: 576px; }
121
+ .container .offset-by-thirteen { padding-left: 624px; }
122
+ .container .offset-by-fourteen { padding-left: 672px; }
123
+ .container .offset-by-fifteen { padding-left: 720px; }
124
+ }
125
+
126
+
127
+ /* #Mobile (Portrait)
128
+ ================================================== */
129
+
130
+ /* Note: Design for a width of 320px */
131
+
132
+ @media only screen and (max-width: 767px) {
133
+ .container { width: 300px; }
134
+ .container .columns,
135
+ .container .column { margin: 0; }
136
+
137
+ .container .one.column,
138
+ .container .one.columns,
139
+ .container .two.columns,
140
+ .container .three.columns,
141
+ .container .four.columns,
142
+ .container .five.columns,
143
+ .container .six.columns,
144
+ .container .seven.columns,
145
+ .container .eight.columns,
146
+ .container .nine.columns,
147
+ .container .ten.columns,
148
+ .container .eleven.columns,
149
+ .container .twelve.columns,
150
+ .container .thirteen.columns,
151
+ .container .fourteen.columns,
152
+ .container .fifteen.columns,
153
+ .container .sixteen.columns,
154
+ .container .one-third.column,
155
+ .container .two-thirds.column { width: 300px; }
156
+
157
+ /* Offsets */
158
+ .container .offset-by-one,
159
+ .container .offset-by-two,
160
+ .container .offset-by-three,
161
+ .container .offset-by-four,
162
+ .container .offset-by-five,
163
+ .container .offset-by-six,
164
+ .container .offset-by-seven,
165
+ .container .offset-by-eight,
166
+ .container .offset-by-nine,
167
+ .container .offset-by-ten,
168
+ .container .offset-by-eleven,
169
+ .container .offset-by-twelve,
170
+ .container .offset-by-thirteen,
171
+ .container .offset-by-fourteen,
172
+ .container .offset-by-fifteen { padding-left: 0; }
173
+
174
+ }
175
+
176
+
177
+ /* #Mobile (Landscape)
178
+ ================================================== */
179
+
180
+ /* Note: Design for a width of 480px */
181
+
182
+ @media only screen and (min-width: 480px) and (max-width: 767px) {
183
+ .container { width: 420px; }
184
+ .container .columns,
185
+ .container .column { margin: 0; }
186
+
187
+ .container .one.column,
188
+ .container .one.columns,
189
+ .container .two.columns,
190
+ .container .three.columns,
191
+ .container .four.columns,
192
+ .container .five.columns,
193
+ .container .six.columns,
194
+ .container .seven.columns,
195
+ .container .eight.columns,
196
+ .container .nine.columns,
197
+ .container .ten.columns,
198
+ .container .eleven.columns,
199
+ .container .twelve.columns,
200
+ .container .thirteen.columns,
201
+ .container .fourteen.columns,
202
+ .container .fifteen.columns,
203
+ .container .sixteen.columns,
204
+ .container .one-third.column,
205
+ .container .two-thirds.column { width: 420px; }
206
+ }
207
+
208
+
209
+ /* #Clearing
210
+ ================================================== */
211
+
212
+ /* Self Clearing Goodness */
213
+ .container:after { content: "\0020"; display: block; height: 0; clear: both; visibility: hidden; }
214
+
215
+ /* Use clearfix class on parent to clear nested columns,
216
+ or wrap each row of columns in a <div class="row"> */
217
+ .clearfix:before,
218
+ .clearfix:after,
219
+ .row:before,
220
+ .row:after {
221
+ content: '\0020';
222
+ display: block;
223
+ overflow: hidden;
224
+ visibility: hidden;
225
+ width: 0;
226
+ height: 0; }
227
+ .row:after,
228
+ .clearfix:after {
229
+ clear: both; }
230
+ .row,
231
+ .clearfix {
232
+ zoom: 1; }
233
+
234
+ /* You can also use a <br class="clear" /> to clear columns */
235
+ .clear {
236
+ clear: both;
237
+ display: block;
238
+ overflow: hidden;
239
+ visibility: hidden;
240
+ width: 0;
241
+ height: 0;
242
+ }
@@ -0,0 +1,12 @@
1
+ class Monologue::Admin::BaseController < Monologue::ApplicationController
2
+ before_filter :authenticate_user!
3
+ force_ssl if Monologue.admin_force_ssl # TODO: find a way to test that with capybara
4
+
5
+ layout "layouts/monologue/admin"
6
+
7
+ def authenticate_user!
8
+ if current_user.nil?
9
+ redirect_to admin_login_url, :alert => "You must first log in to access admin section."
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,54 @@
1
+ class Monologue::Admin::PostsController < Monologue::Admin::BaseController
2
+ respond_to :html
3
+ cache_sweeper Monologue::PostsSweeper, :only => [:create, :update, :destroy]
4
+
5
+ def index
6
+ @posts = Monologue::Post.default
7
+ end
8
+
9
+ def new
10
+ @post = Monologue::Post.new
11
+ @revision = @post.posts_revisions.build
12
+ end
13
+
14
+ def create
15
+ params[:post][:posts_revisions_attributes] = {}
16
+ params[:post][:posts_revisions_attributes][0] = params[:post][:posts_revision]
17
+ params[:post].delete("posts_revision")
18
+ @post = Monologue::Post.new(params[:post])
19
+ @revision = @post.posts_revisions.first
20
+ @revision.user_id = current_user.id
21
+
22
+ if @post.save
23
+ redirect_to edit_admin_post_path(@post), :notice => 'Monologue created'
24
+ else
25
+ render :action => "new"
26
+ end
27
+ end
28
+
29
+ def edit
30
+ @post = Monologue::Post.includes(:posts_revisions).find(params[:id])
31
+ @revision = @post.posts_revisions.last
32
+ end
33
+
34
+ def update
35
+ @post = Monologue::Post.includes(:posts_revisions).find(params[:id])
36
+ @post.published = params[:post][:published]
37
+ @revision = @post.posts_revisions.build(params[:post][:posts_revision])
38
+ @revision.user_id = current_user.id
39
+ if @post.save
40
+ redirect_to edit_admin_post_path(@post), :notice => 'Monologue saved'
41
+ else
42
+ render :edit
43
+ end
44
+ end
45
+
46
+ def destroy
47
+ post = Monologue::Post.find(params[:id])
48
+ if post.destroy
49
+ redirect_to admin_posts_path, :notice => "Monologue removed"
50
+ else
51
+ redirect_to admin_posts_path, :alert => "Failed to remove monologue!"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,22 @@
1
+ class Monologue::Admin::SessionsController < Monologue::Admin::BaseController
2
+ skip_before_filter :authenticate_user!
3
+
4
+ def new
5
+ end
6
+
7
+ def create
8
+ user = Monologue::User.find_by_email(params[:email])
9
+ if user && user.authenticate(params[:password])
10
+ session[:user_id] = user.id
11
+ redirect_to admin_url, :notice => t("monologue.admin.sessions.messages.logged_in")
12
+ else
13
+ flash.now.alert = t("monologue.admin.sessions.messages.invalid")
14
+ render "new"
15
+ end
16
+ end
17
+
18
+ def destroy
19
+ session[:user_id] = nil
20
+ redirect_to admin_url, :notice => t("monologue.admin.sessions.messages.logged_out")
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ class Monologue::ApplicationController < ApplicationController
2
+
3
+ def not_found
4
+ # fallback to the default 404.html page from main_app.
5
+ file = Rails.root.join('public', '404.html')
6
+ if file.exist?
7
+ render :file => file.cleanpath.to_s.gsub(%r{#{file.extname}$}, ''),
8
+ :layout => false, :status => 404, :formats => [:html]
9
+ else
10
+ render :action => "404", :status => 404, :formats => [:html]
11
+ end
12
+ end
13
+
14
+
15
+ private
16
+
17
+ def current_user
18
+ @current_user ||= Monologue::User.find(session[:user_id]) if session[:user_id]
19
+ end
20
+
21
+ helper_method :current_user
22
+ end
@@ -0,0 +1,25 @@
1
+ class Monologue::PostsController < Monologue::ApplicationController
2
+ caches_page :index, :show, :feed , :if => Proc.new { current_user.nil? }
3
+
4
+ def index
5
+ @page = params[:page].nil? ? 1 : params[:page]
6
+ @posts = Monologue::Post.published.page(@page)
7
+ end
8
+
9
+ def show
10
+ unless current_user
11
+ post = Monologue::Post.published.where("monologue_posts_revisions.url = :url", {:url => root_path + params[:post_url]}).first
12
+ else
13
+ post = Monologue::Post.default.where("monologue_posts_revisions.url = :url", {:url => root_path + params[:post_url]}).first
14
+ end
15
+ if post.nil?
16
+ not_found
17
+ return
18
+ end
19
+ @revision = post.posts_revisions.first
20
+ end
21
+
22
+ def feed
23
+ @posts = Monologue::Post.published.limit(25)
24
+ end
25
+ end
@@ -0,0 +1,61 @@
1
+ class MonologueAdminFormBuilder < ActionView::Helpers::FormBuilder
2
+ delegate :content_tag, :tag, :to => :@template
3
+
4
+ %w[text_field text_area password_field collection_select select file_field].each do |method_name|
5
+ define_method(method_name) do |name, *args|
6
+ content_tag :div, :class => "field" do
7
+ field_label(name, *args) + super(name, *args)
8
+ end
9
+ end
10
+ end
11
+
12
+ def check_box(name, *args)
13
+ options = args.extract_options!
14
+ required = object.class.validators_on(name).any? { |v| v.kind_of? ActiveModel::Validations::PresenceValidator }
15
+ content_tag :label, :class => (required ? "required checkbox inline" : "checkbox inline") do
16
+ super + (options[:label] || name)
17
+ end
18
+ end
19
+
20
+ def collection_check_boxes(attribute, records, record_id, record_name)
21
+ content_tag :div, :class => "field" do
22
+ @template.hidden_field_tag("#{object_name}[#{attribute}][]") +
23
+ records.map do |record|
24
+ element_id = "#{object_name}_#{attribute}_#{record.send(record_id)}"
25
+ checkbox = @template.check_box_tag("#{object_name}[#{attribute}][]", record.send(record_id), object.send(attribute).include?(record.send(record_id)), :id => element_id)
26
+ checkbox + " " + @template.label_tag(element_id, record.send(record_name))
27
+ end.join(tag(:br)).html_safe
28
+ end
29
+ end
30
+
31
+ def submit(*args)
32
+ content_tag :div, :class => "actions" do
33
+ super
34
+ end
35
+ end
36
+
37
+ def error_messages
38
+ if object.errors.full_messages.any?
39
+ content_tag(:div, :class => "alert alert-error error_messages") do
40
+ content_tag(:strong, "Invalid Fields") +
41
+ content_tag(:ul) do
42
+ object.errors.messages.map do |msg|
43
+ content_tag(:li, msg[1][0])
44
+ end.join.html_safe
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def field_label(name, *args)
53
+ options = args.extract_options!
54
+ required = object.class.validators_on(name).any? { |v| v.kind_of? ActiveModel::Validations::PresenceValidator }
55
+ label(name, options[:label], :class => ("required" if required))
56
+ end
57
+
58
+ def objectify_options(options)
59
+ super.except(:label)
60
+ end
61
+ end