monologue 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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