about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock364
-rw-r--r--app/assets/javascripts/main/comments.coffee1
-rw-r--r--app/assets/stylesheets/main/entries.scss71
-rw-r--r--app/assets/stylesheets/main/layout.scss51
-rw-r--r--app/controllers/blogs_controller.rb7
-rw-r--r--app/controllers/comments_controller.rb9
-rw-r--r--app/controllers/quotes_controller.rb14
-rw-r--r--app/controllers/streams_controller.rb19
-rw-r--r--app/helpers/application_helper.rb19
-rw-r--r--app/models/blog.rb2
-rw-r--r--app/models/quote.rb9
-rw-r--r--app/models/stream.rb11
-rw-r--r--app/models/update.rb8
-rw-r--r--app/views/admin/blogs/_form.html.haml10
-rw-r--r--app/views/admin/streams/edit.html.haml2
-rw-r--r--app/views/admin/streams/index.html.haml4
-rw-r--r--app/views/admin/updates/edit.html.haml2
-rw-r--r--app/views/comments/_form.html.haml3
-rw-r--r--app/views/layouts/application.html.haml15
-rw-r--r--app/views/layouts/quotes.html.haml1
-rw-r--r--app/views/quotes/search_form.html.haml6
-rw-r--r--app/views/streams/_stream.html.haml8
-rw-r--r--app/views/streams/index.html.haml19
-rw-r--r--app/views/streams/show.html.haml2
-rw-r--r--app/views/updates/_update.html.haml7
-rw-r--r--config/credentials.yml.enc2
-rw-r--r--config/credentials/production.yml.enc2
-rw-r--r--config/deploy.rb2
-rw-r--r--config/initializers/recaptcha.rb4
-rw-r--r--config/routes.rb3
-rw-r--r--db/migrate/20250112025207_widen_blog_body_column.rb5
-rw-r--r--db/migrate/20250512181245_add_latest_post_at_to_stream.rb21
-rw-r--r--db/schema.rb5
34 files changed, 495 insertions, 215 deletions
diff --git a/Gemfile b/Gemfile index d6ca5e9..5fa14f7 100644 --- a/Gemfile +++ b/Gemfile
@@ -87,3 +87,5 @@ gem 'rails_autolink'
87gem 'whenever', "~> 1.0.0", require: false 87gem 'whenever', "~> 1.0.0", require: false
88gem "webmention" 88gem "webmention"
89gem 'microformats', '~> 4.0', '>= 4.2.1' 89gem 'microformats', '~> 4.0', '>= 4.2.1'
90gem 'recaptcha'
91gem 'ransack'
diff --git a/Gemfile.lock b/Gemfile.lock index cc60005..e2e85e9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock
@@ -1,111 +1,118 @@
1GEM 1GEM
2 remote: https://rubygems.org/ 2 remote: https://rubygems.org/
3 specs: 3 specs:
4 actioncable (7.1.1) 4 actioncable (7.1.5.1)
5 actionpack (= 7.1.1) 5 actionpack (= 7.1.5.1)
6 activesupport (= 7.1.1) 6 activesupport (= 7.1.5.1)
7 nio4r (~> 2.0) 7 nio4r (~> 2.0)
8 websocket-driver (>= 0.6.1) 8 websocket-driver (>= 0.6.1)
9 zeitwerk (~> 2.6) 9 zeitwerk (~> 2.6)
10 actionmailbox (7.1.1) 10 actionmailbox (7.1.5.1)
11 actionpack (= 7.1.1) 11 actionpack (= 7.1.5.1)
12 activejob (= 7.1.1) 12 activejob (= 7.1.5.1)
13 activerecord (= 7.1.1) 13 activerecord (= 7.1.5.1)
14 activestorage (= 7.1.1) 14 activestorage (= 7.1.5.1)
15 activesupport (= 7.1.1) 15 activesupport (= 7.1.5.1)
16 mail (>= 2.7.1) 16 mail (>= 2.7.1)
17 net-imap 17 net-imap
18 net-pop 18 net-pop
19 net-smtp 19 net-smtp
20 actionmailer (7.1.1) 20 actionmailer (7.1.5.1)
21 actionpack (= 7.1.1) 21 actionpack (= 7.1.5.1)
22 actionview (= 7.1.1) 22 actionview (= 7.1.5.1)
23 activejob (= 7.1.1) 23 activejob (= 7.1.5.1)
24 activesupport (= 7.1.1) 24 activesupport (= 7.1.5.1)
25 mail (~> 2.5, >= 2.5.4) 25 mail (~> 2.5, >= 2.5.4)
26 net-imap 26 net-imap
27 net-pop 27 net-pop
28 net-smtp 28 net-smtp
29 rails-dom-testing (~> 2.2) 29 rails-dom-testing (~> 2.2)
30 actionpack (7.1.1) 30 actionpack (7.1.5.1)
31 actionview (= 7.1.1) 31 actionview (= 7.1.5.1)
32 activesupport (= 7.1.1) 32 activesupport (= 7.1.5.1)
33 nokogiri (>= 1.8.5) 33 nokogiri (>= 1.8.5)
34 racc
34 rack (>= 2.2.4) 35 rack (>= 2.2.4)
35 rack-session (>= 1.0.1) 36 rack-session (>= 1.0.1)
36 rack-test (>= 0.6.3) 37 rack-test (>= 0.6.3)
37 rails-dom-testing (~> 2.2) 38 rails-dom-testing (~> 2.2)
38 rails-html-sanitizer (~> 1.6) 39 rails-html-sanitizer (~> 1.6)
39 actiontext (7.1.1) 40 actiontext (7.1.5.1)
40 actionpack (= 7.1.1) 41 actionpack (= 7.1.5.1)
41 activerecord (= 7.1.1) 42 activerecord (= 7.1.5.1)
42 activestorage (= 7.1.1) 43 activestorage (= 7.1.5.1)
43 activesupport (= 7.1.1) 44 activesupport (= 7.1.5.1)
44 globalid (>= 0.6.0) 45 globalid (>= 0.6.0)
45 nokogiri (>= 1.8.5) 46 nokogiri (>= 1.8.5)
46 actionview (7.1.1) 47 actionview (7.1.5.1)
47 activesupport (= 7.1.1) 48 activesupport (= 7.1.5.1)
48 builder (~> 3.1) 49 builder (~> 3.1)
49 erubi (~> 1.11) 50 erubi (~> 1.11)
50 rails-dom-testing (~> 2.2) 51 rails-dom-testing (~> 2.2)
51 rails-html-sanitizer (~> 1.6) 52 rails-html-sanitizer (~> 1.6)
52 active_storage_validations (1.1.1) 53 active_storage_validations (2.0.3)
53 activejob (>= 5.2.0) 54 activejob (>= 6.1.4)
54 activemodel (>= 5.2.0) 55 activemodel (>= 6.1.4)
55 activestorage (>= 5.2.0) 56 activestorage (>= 6.1.4)
56 activesupport (>= 5.2.0) 57 activesupport (>= 6.1.4)
57 activejob (7.1.1) 58 marcel (>= 1.0.3)
58 activesupport (= 7.1.1) 59 activejob (7.1.5.1)
60 activesupport (= 7.1.5.1)
59 globalid (>= 0.3.6) 61 globalid (>= 0.3.6)
60 activemodel (7.1.1) 62 activemodel (7.1.5.1)
61 activesupport (= 7.1.1) 63 activesupport (= 7.1.5.1)
62 activerecord (7.1.1) 64 activerecord (7.1.5.1)
63 activemodel (= 7.1.1) 65 activemodel (= 7.1.5.1)
64 activesupport (= 7.1.1) 66 activesupport (= 7.1.5.1)
65 timeout (>= 0.4.0) 67 timeout (>= 0.4.0)
66 activestorage (7.1.1) 68 activestorage (7.1.5.1)
67 actionpack (= 7.1.1) 69 actionpack (= 7.1.5.1)
68 activejob (= 7.1.1) 70 activejob (= 7.1.5.1)
69 activerecord (= 7.1.1) 71 activerecord (= 7.1.5.1)
70 activesupport (= 7.1.1) 72 activesupport (= 7.1.5.1)
71 marcel (~> 1.0) 73 marcel (~> 1.0)
72 activesupport (7.1.1) 74 activesupport (7.1.5.1)
73 base64 75 base64
76 benchmark (>= 0.3)
74 bigdecimal 77 bigdecimal
75 concurrent-ruby (~> 1.0, >= 1.0.2) 78 concurrent-ruby (~> 1.0, >= 1.0.2)
76 connection_pool (>= 2.2.5) 79 connection_pool (>= 2.2.5)
77 drb 80 drb
78 i18n (>= 1.6, < 2) 81 i18n (>= 1.6, < 2)
82 logger (>= 1.4.2)
79 minitest (>= 5.1) 83 minitest (>= 5.1)
80 mutex_m 84 mutex_m
85 securerandom (>= 0.3)
81 tzinfo (~> 2.0) 86 tzinfo (~> 2.0)
82 acts-as-taggable-on (10.0.0) 87 acts-as-taggable-on (12.0.0)
83 activerecord (>= 6.1, < 7.2) 88 activerecord (>= 7.1, < 8.1)
84 addressable (2.8.5) 89 zeitwerk (>= 2.4, < 3.0)
85 public_suffix (>= 2.0.2, < 6.0) 90 addressable (2.8.7)
86 airbrussh (1.5.0) 91 public_suffix (>= 2.0.2, < 7.0)
92 airbrussh (1.5.3)
87 sshkit (>= 1.6.1, != 1.7.0) 93 sshkit (>= 1.6.1, != 1.7.0)
88 akismet (3.0.0) 94 akismet (3.0.0)
89 audited (5.4.0) 95 audited (5.8.0)
90 activerecord (>= 5.0, < 7.2) 96 activerecord (>= 5.2, < 8.2)
91 request_store (~> 1.2) 97 activesupport (>= 5.2, < 8.2)
92 base64 (0.2.0) 98 base64 (0.2.0)
93 bcrypt (3.1.19) 99 bcrypt (3.1.20)
94 bcrypt_pbkdf (1.1.1) 100 bcrypt_pbkdf (1.1.1)
95 bigdecimal (3.1.4) 101 benchmark (0.4.0)
102 bigdecimal (3.1.9)
96 bindex (0.8.1) 103 bindex (0.8.1)
97 builder (3.2.4) 104 builder (3.3.0)
98 byebug (11.1.3) 105 byebug (12.0.0)
99 capistrano (3.18.0) 106 capistrano (3.19.2)
100 airbrussh (>= 1.0.0) 107 airbrussh (>= 1.0.0)
101 i18n 108 i18n
102 rake (>= 10.0.0) 109 rake (>= 10.0.0)
103 sshkit (>= 1.9.0) 110 sshkit (>= 1.9.0)
104 capistrano-bundler (2.1.0) 111 capistrano-bundler (2.1.1)
105 capistrano (~> 3.1) 112 capistrano (~> 3.1)
106 capistrano-passenger (0.2.1) 113 capistrano-passenger (0.2.1)
107 capistrano (~> 3.0) 114 capistrano (~> 3.0)
108 capistrano-rails (1.6.3) 115 capistrano-rails (1.7.0)
109 capistrano (~> 3.1) 116 capistrano (~> 3.1)
110 capistrano-bundler (>= 1.1, < 3) 117 capistrano-bundler (>= 1.1, < 3)
111 capistrano-rvm (0.1.2) 118 capistrano-rvm (0.1.2)
@@ -127,35 +134,35 @@ GEM
127 coffee-script-source 134 coffee-script-source
128 execjs 135 execjs
129 coffee-script-source (1.12.2) 136 coffee-script-source (1.12.2)
130 concurrent-ruby (1.2.2) 137 concurrent-ruby (1.3.5)
131 connection_pool (2.4.1) 138 connection_pool (2.5.3)
132 crass (1.0.6) 139 crass (1.0.6)
133 date (3.3.4) 140 date (3.4.1)
134 devise (4.9.3) 141 devise (4.9.4)
135 bcrypt (~> 3.0) 142 bcrypt (~> 3.0)
136 orm_adapter (~> 0.1) 143 orm_adapter (~> 0.1)
137 railties (>= 4.1.0) 144 railties (>= 4.1.0)
138 responders 145 responders
139 warden (~> 1.2.3) 146 warden (~> 1.2.3)
140 domain_name (0.6.20240107) 147 domain_name (0.6.20240107)
141 drb (2.2.0) 148 drb (2.2.1)
142 ruby2_keywords 149 ed25519 (1.4.0)
143 ed25519 (1.3.0) 150 enumerize (2.8.1)
144 enumerize (2.7.0)
145 activesupport (>= 3.2) 151 activesupport (>= 3.2)
146 erubi (1.12.0) 152 erubi (1.13.1)
147 execjs (2.9.1) 153 execjs (2.10.0)
148 ffi (1.16.3) 154 ffi (1.17.2)
149 ffi-compiler (1.3.2) 155 ffi-compiler (1.3.2)
150 ffi (>= 1.15.5) 156 ffi (>= 1.15.5)
151 rake 157 rake
152 globalid (1.2.1) 158 globalid (1.2.1)
153 activesupport (>= 6.1) 159 activesupport (>= 6.1)
154 haml (6.2.3) 160 haml (6.3.0)
155 temple (>= 0.8.2) 161 temple (>= 0.8.2)
156 thor 162 thor
157 tilt 163 tilt
158 highline (2.1.0) 164 highline (3.1.2)
165 reline
159 http (5.2.0) 166 http (5.2.0)
160 addressable (~> 2.8) 167 addressable (~> 2.8)
161 base64 (~> 0.1) 168 base64 (~> 0.1)
@@ -165,40 +172,43 @@ GEM
165 http-cookie (1.0.8) 172 http-cookie (1.0.8)
166 domain_name (~> 0.5) 173 domain_name (~> 0.5)
167 http-form_data (2.3.0) 174 http-form_data (2.3.0)
168 i18n (1.14.1) 175 i18n (1.14.7)
169 concurrent-ruby (~> 1.0) 176 concurrent-ruby (~> 1.0)
170 image_processing (1.12.2) 177 image_processing (1.14.0)
171 mini_magick (>= 4.9.5, < 5) 178 mini_magick (>= 4.9.5, < 6)
172 ruby-vips (>= 2.0.17, < 3) 179 ruby-vips (>= 2.0.17, < 3)
173 indieweb-endpoints (8.0.0) 180 indieweb-endpoints (9.1.0)
174 http (~> 5.0) 181 http (~> 5.2)
175 link-header-parser (~> 5.0) 182 link-header-parser (~> 6.1)
176 nokogiri (>= 1.13) 183 nokogiri (>= 1.13)
177 io-console (0.6.0) 184 io-console (0.8.0)
178 irb (1.8.3) 185 irb (1.15.2)
179 rdoc 186 pp (>= 0.6.0)
180 reline (>= 0.3.8) 187 rdoc (>= 4.0.0)
181 jbuilder (2.11.5) 188 reline (>= 0.4.2)
189 jbuilder (2.13.0)
182 actionview (>= 5.0.0) 190 actionview (>= 5.0.0)
183 activesupport (>= 5.0.0) 191 activesupport (>= 5.0.0)
184 jquery-rails (4.6.0) 192 jquery-rails (4.6.0)
185 rails-dom-testing (>= 1, < 3) 193 rails-dom-testing (>= 1, < 3)
186 railties (>= 4.2.0) 194 railties (>= 4.2.0)
187 thor (>= 0.14, < 2.0) 195 thor (>= 0.14, < 2.0)
188 jquery-ui-rails (6.0.1) 196 jquery-ui-rails (8.0.0)
189 railties (>= 3.2.16) 197 railties (>= 3.2.16)
190 js-routes (2.2.7) 198 js-routes (2.3.5)
191 railties (>= 4) 199 railties (>= 5)
192 json (2.9.0) 200 sorbet-runtime
193 libv8-node (18.16.0.0) 201 json (2.12.0)
194 link-header-parser (5.1.1) 202 libv8-node (23.6.1.0)
203 link-header-parser (6.1.1)
195 listen (3.0.8) 204 listen (3.0.8)
196 rb-fsevent (~> 0.9, >= 0.9.4) 205 rb-fsevent (~> 0.9, >= 0.9.4)
197 rb-inotify (~> 0.9, >= 0.9.7) 206 rb-inotify (~> 0.9, >= 0.9.7)
198 llhttp-ffi (0.5.0) 207 llhttp-ffi (0.5.1)
199 ffi-compiler (~> 1.0) 208 ffi-compiler (~> 1.0)
200 rake (~> 13.0) 209 rake (~> 13.0)
201 loofah (2.21.4) 210 logger (1.7.0)
211 loofah (2.24.1)
202 crass (~> 1.0.2) 212 crass (~> 1.0.2)
203 nokogiri (>= 1.12.0) 213 nokogiri (>= 1.12.0)
204 mail (2.8.1) 214 mail (2.8.1)
@@ -206,115 +216,128 @@ GEM
206 net-imap 216 net-imap
207 net-pop 217 net-pop
208 net-smtp 218 net-smtp
209 marcel (1.0.2) 219 marcel (1.0.4)
210 meta-tags (2.19.0) 220 meta-tags (2.22.1)
211 actionpack (>= 3.2.0, < 7.2) 221 actionpack (>= 6.0.0, < 8.1)
212 microformats (4.5.0) 222 microformats (4.5.0)
213 json 223 json
214 nokogiri 224 nokogiri
215 mime-types (3.5.1) 225 mime-types (3.7.0)
216 mime-types-data (~> 3.2015) 226 logger
217 mime-types-data (3.2023.1003) 227 mime-types-data (~> 3.2025, >= 3.2025.0507)
228 mime-types-data (3.2025.0507)
218 mimemagic (0.3.10) 229 mimemagic (0.3.10)
219 nokogiri (~> 1) 230 nokogiri (~> 1)
220 rake 231 rake
221 mini_magick (4.12.0) 232 mini_magick (5.2.0)
233 benchmark
234 logger
222 mini_mime (1.1.5) 235 mini_mime (1.1.5)
223 mini_portile2 (2.8.5) 236 mini_portile2 (2.8.9)
224 mini_racer (0.8.0) 237 mini_racer (0.18.1)
225 libv8-node (~> 18.16.0.0) 238 libv8-node (~> 23.6.1.0)
226 minitest (5.20.0) 239 minitest (5.25.5)
227 mutex_m (0.2.0) 240 mutex_m (0.3.0)
228 mysql2 (0.5.5) 241 mysql2 (0.5.6)
229 net-imap (0.4.4) 242 net-imap (0.5.8)
230 date 243 date
231 net-protocol 244 net-protocol
232 net-pop (0.1.2) 245 net-pop (0.1.2)
233 net-protocol 246 net-protocol
234 net-protocol (0.2.2) 247 net-protocol (0.2.2)
235 timeout 248 timeout
236 net-scp (4.0.0) 249 net-scp (4.1.0)
237 net-ssh (>= 2.6.5, < 8.0.0) 250 net-ssh (>= 2.6.5, < 8.0.0)
238 net-smtp (0.4.0) 251 net-sftp (4.0.0)
252 net-ssh (>= 5.0.0, < 8.0.0)
253 net-smtp (0.5.1)
239 net-protocol 254 net-protocol
240 net-ssh (7.2.0) 255 net-ssh (7.3.0)
241 nio4r (2.5.9) 256 nio4r (2.7.4)
242 nokogiri (1.15.4) 257 nokogiri (1.18.8)
243 mini_portile2 (~> 2.8.2) 258 mini_portile2 (~> 2.8.2)
244 racc (~> 1.4) 259 racc (~> 1.4)
245 normalize-rails (8.0.1) 260 normalize-rails (8.0.1)
246 orm_adapter (0.5.0) 261 orm_adapter (0.5.0)
262 ostruct (0.6.1)
247 paperclip (6.1.0) 263 paperclip (6.1.0)
248 activemodel (>= 4.2.0) 264 activemodel (>= 4.2.0)
249 activesupport (>= 4.2.0) 265 activesupport (>= 4.2.0)
250 mime-types 266 mime-types
251 mimemagic (~> 0.3.0) 267 mimemagic (~> 0.3.0)
252 terrapin (~> 0.6.0) 268 terrapin (~> 0.6.0)
253 psych (5.1.1.1) 269 pp (0.6.2)
270 prettyprint
271 prettyprint (0.2.0)
272 psych (5.2.6)
273 date
254 stringio 274 stringio
255 public_suffix (5.0.3) 275 public_suffix (6.0.2)
256 racc (1.7.3) 276 racc (1.8.1)
257 rack (2.2.8) 277 rack (2.2.14)
258 rack-session (1.0.1) 278 rack-session (1.0.2)
259 rack (< 3) 279 rack (< 3)
260 rack-test (2.1.0) 280 rack-test (2.2.0)
261 rack (>= 1.3) 281 rack (>= 1.3)
262 rackup (1.0.0) 282 rackup (1.0.1)
263 rack (< 3) 283 rack (< 3)
264 webrick 284 webrick
265 rails (7.1.1) 285 rails (7.1.5.1)
266 actioncable (= 7.1.1) 286 actioncable (= 7.1.5.1)
267 actionmailbox (= 7.1.1) 287 actionmailbox (= 7.1.5.1)
268 actionmailer (= 7.1.1) 288 actionmailer (= 7.1.5.1)
269 actionpack (= 7.1.1) 289 actionpack (= 7.1.5.1)
270 actiontext (= 7.1.1) 290 actiontext (= 7.1.5.1)
271 actionview (= 7.1.1) 291 actionview (= 7.1.5.1)
272 activejob (= 7.1.1) 292 activejob (= 7.1.5.1)
273 activemodel (= 7.1.1) 293 activemodel (= 7.1.5.1)
274 activerecord (= 7.1.1) 294 activerecord (= 7.1.5.1)
275 activestorage (= 7.1.1) 295 activestorage (= 7.1.5.1)
276 activesupport (= 7.1.1) 296 activesupport (= 7.1.5.1)
277 bundler (>= 1.15.0) 297 bundler (>= 1.15.0)
278 railties (= 7.1.1) 298 railties (= 7.1.5.1)
279 rails-dom-testing (2.2.0) 299 rails-dom-testing (2.2.0)
280 activesupport (>= 5.0.0) 300 activesupport (>= 5.0.0)
281 minitest 301 minitest
282 nokogiri (>= 1.6) 302 nokogiri (>= 1.6)
283 rails-html-sanitizer (1.6.0) 303 rails-html-sanitizer (1.6.2)
284 loofah (~> 2.21) 304 loofah (~> 2.21)
285 nokogiri (~> 1.14) 305 nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
286 rails_autolink (1.1.8) 306 rails_autolink (1.1.8)
287 actionview (> 3.1) 307 actionview (> 3.1)
288 activesupport (> 3.1) 308 activesupport (> 3.1)
289 railties (> 3.1) 309 railties (> 3.1)
290 railties (7.1.1) 310 railties (7.1.5.1)
291 actionpack (= 7.1.1) 311 actionpack (= 7.1.5.1)
292 activesupport (= 7.1.1) 312 activesupport (= 7.1.5.1)
293 irb 313 irb
294 rackup (>= 1.0.0) 314 rackup (>= 1.0.0)
295 rake (>= 12.2) 315 rake (>= 12.2)
296 thor (~> 1.0, >= 1.2.2) 316 thor (~> 1.0, >= 1.2.2)
297 zeitwerk (~> 2.6) 317 zeitwerk (~> 2.6)
298 rake (13.1.0) 318 rake (13.2.1)
319 ransack (4.3.0)
320 activerecord (>= 6.1.5)
321 activesupport (>= 6.1.5)
322 i18n
299 rb-fsevent (0.11.2) 323 rb-fsevent (0.11.2)
300 rb-inotify (0.10.1) 324 rb-inotify (0.11.1)
301 ffi (~> 1.0) 325 ffi (~> 1.0)
302 rdoc (6.6.0) 326 rdoc (6.13.1)
303 psych (>= 4.0.0) 327 psych (>= 4.0.0)
304 redcarpet (3.6.0) 328 recaptcha (5.19.0)
305 reline (0.3.9) 329 redcarpet (3.6.1)
330 reline (0.6.1)
306 io-console (~> 0.5) 331 io-console (~> 0.5)
307 request_store (1.5.1)
308 rack (>= 1.4)
309 responders (3.1.1) 332 responders (3.1.1)
310 actionpack (>= 5.2) 333 actionpack (>= 5.2)
311 railties (>= 5.2) 334 railties (>= 5.2)
312 rexml (3.2.6) 335 rexml (3.4.1)
313 rouge (4.2.0) 336 rouge (4.5.2)
314 ruby-vips (2.2.0) 337 ruby-vips (2.2.3)
315 ffi (~> 1.12) 338 ffi (~> 1.12)
316 ruby2_keywords (0.0.5) 339 logger
317 rubyzip (2.3.2) 340 rubyzip (2.4.1)
318 sassc (2.4.0) 341 sassc (2.4.0)
319 ffi (~> 1.9) 342 ffi (~> 1.9)
320 sassc-rails (2.1.2) 343 sassc-rails (2.1.2)
@@ -323,10 +346,14 @@ GEM
323 sprockets (> 3.0) 346 sprockets (> 3.0)
324 sprockets-rails 347 sprockets-rails
325 tilt 348 tilt
326 selenium-webdriver (4.14.0) 349 securerandom (0.4.1)
350 selenium-webdriver (4.32.0)
351 base64 (~> 0.2)
352 logger (~> 1.4)
327 rexml (~> 3.2, >= 3.2.5) 353 rexml (~> 3.2, >= 3.2.5)
328 rubyzip (>= 1.2.2, < 3.0) 354 rubyzip (>= 1.2.2, < 3.0)
329 websocket (~> 1.0) 355 websocket (~> 1.0)
356 sorbet-runtime (0.5.12087)
330 spring (2.1.1) 357 spring (2.1.1)
331 spring-watcher-listen (2.0.1) 358 spring-watcher-listen (2.0.1)
332 listen (>= 2.7, < 4.0) 359 listen (>= 2.7, < 4.0)
@@ -334,24 +361,28 @@ GEM
334 sprockets (3.7.2) 361 sprockets (3.7.2)
335 concurrent-ruby (~> 1.0) 362 concurrent-ruby (~> 1.0)
336 rack (> 1, < 3) 363 rack (> 1, < 3)
337 sprockets-rails (3.4.2) 364 sprockets-rails (3.5.2)
338 actionpack (>= 5.2) 365 actionpack (>= 6.1)
339 activesupport (>= 5.2) 366 activesupport (>= 6.1)
340 sprockets (>= 3.0.0) 367 sprockets (>= 3.0.0)
341 sqlite3 (1.6.7) 368 sqlite3 (2.6.0)
342 mini_portile2 (~> 2.8.0) 369 mini_portile2 (~> 2.8.0)
343 sshkit (1.21.5) 370 sshkit (1.24.0)
371 base64
372 logger
344 net-scp (>= 1.1.2) 373 net-scp (>= 1.1.2)
374 net-sftp (>= 2.1.2)
345 net-ssh (>= 2.8.0) 375 net-ssh (>= 2.8.0)
346 stringio (3.0.8) 376 ostruct
377 stringio (3.1.7)
347 temple (0.10.3) 378 temple (0.10.3)
348 terrapin (0.6.0) 379 terrapin (0.6.0)
349 climate_control (>= 0.0.3, < 1.0) 380 climate_control (>= 0.0.3, < 1.0)
350 terser (1.1.19) 381 terser (1.1.20)
351 execjs (>= 0.3.0, < 3) 382 execjs (>= 0.3.0, < 3)
352 thor (1.3.0) 383 thor (1.3.2)
353 tilt (2.3.0) 384 tilt (2.6.0)
354 timeout (0.4.1) 385 timeout (0.4.3)
355 turbolinks (5.2.1) 386 turbolinks (5.2.1)
356 turbolinks-source (~> 5.2) 387 turbolinks-source (~> 5.2)
357 turbolinks-source (5.2.0) 388 turbolinks-source (5.2.0)
@@ -364,21 +395,22 @@ GEM
364 activemodel (>= 6.0.0) 395 activemodel (>= 6.0.0)
365 bindex (>= 0.4.0) 396 bindex (>= 0.4.0)
366 railties (>= 6.0.0) 397 railties (>= 6.0.0)
367 webmention (7.0.0) 398 webmention (8.0.0)
368 http (~> 5.0) 399 http (~> 5.2)
369 indieweb-endpoints (~> 8.0) 400 indieweb-endpoints (~> 9.0)
370 nokogiri (>= 1.13) 401 nokogiri (>= 1.16)
371 webrick (1.8.1) 402 webrick (1.9.1)
372 websocket (1.2.10) 403 websocket (1.2.11)
373 websocket-driver (0.7.6) 404 websocket-driver (0.7.7)
405 base64
374 websocket-extensions (>= 0.1.0) 406 websocket-extensions (>= 0.1.0)
375 websocket-extensions (0.1.5) 407 websocket-extensions (0.1.5)
376 whenever (1.0.0) 408 whenever (1.0.0)
377 chronic (>= 0.6.3) 409 chronic (>= 0.6.3)
378 will_paginate (4.0.0) 410 will_paginate (4.0.1)
379 xpath (3.2.0) 411 xpath (3.2.0)
380 nokogiri (~> 1.8) 412 nokogiri (~> 1.8)
381 zeitwerk (2.6.12) 413 zeitwerk (2.7.2)
382 414
383PLATFORMS 415PLATFORMS
384 ruby 416 ruby
@@ -416,6 +448,8 @@ DEPENDENCIES
416 paperclip 448 paperclip
417 rails (~> 7.1.0) 449 rails (~> 7.1.0)
418 rails_autolink 450 rails_autolink
451 ransack
452 recaptcha
419 redcarpet 453 redcarpet
420 rouge 454 rouge
421 sassc-rails 455 sassc-rails
diff --git a/app/assets/javascripts/main/comments.coffee b/app/assets/javascripts/main/comments.coffee index d33e200..08aa794 100644 --- a/app/assets/javascripts/main/comments.coffee +++ b/app/assets/javascripts/main/comments.coffee
@@ -10,5 +10,4 @@ comments_ready = ->
10 $("#comment-reply-msg").hide() 10 $("#comment-reply-msg").hide()
11 return false 11 return false
12 12
13$(document).ready(comments_ready)
14$(document).on('turbolinks:load', comments_ready) 13$(document).on('turbolinks:load', comments_ready)
diff --git a/app/assets/stylesheets/main/entries.scss b/app/assets/stylesheets/main/entries.scss index 37b0a49..d67ae67 100644 --- a/app/assets/stylesheets/main/entries.scss +++ b/app/assets/stylesheets/main/entries.scss
@@ -3,9 +3,9 @@
3// You can use Sass (SCSS) here: http://sass-lang.com/ 3// You can use Sass (SCSS) here: http://sass-lang.com/
4 4
5.blog-post { 5.blog-post {
6 font-size: 16px; 6 line-height: 1.5em;
7 line-height: 24px;
8 margin: 0 1em 1em; 7 margin: 0 1em 1em;
8 clear: both;
9 9
10 .blog-title { 10 .blog-title {
11 flex-grow: 1; 11 flex-grow: 1;
@@ -41,6 +41,7 @@
41 .post-calendar { 41 .post-calendar {
42 width: 45px; 42 width: 45px;
43 height: 49px; 43 height: 49px;
44 flex-shrink: 0;
44 45
45 &.post-date-1 { 46 &.post-date-1 {
46 background: image-url("date-bg-1.gif") no-repeat; 47 background: image-url("date-bg-1.gif") no-repeat;
@@ -152,7 +153,6 @@
152 153
153 .update-posted { 154 .update-posted {
154 display: block; 155 display: block;
155 font-style: italic;
156 background-color: #EAADEA; 156 background-color: #EAADEA;
157 font-size: 16px; 157 font-size: 16px;
158 margin: .5em -20px; 158 margin: .5em -20px;
@@ -160,6 +160,16 @@
160 border-width: 1px 0 1px 0; 160 border-width: 1px 0 1px 0;
161 border-style: solid; 161 border-style: solid;
162 border-color: #DB70DB; 162 border-color: #DB70DB;
163
164 time {
165 font-style: italic;
166 }
167
168 .update-edit-link {
169 float: right;
170 color: blue;
171 font-weight: normal;
172 }
163 } 173 }
164 } 174 }
165} 175}
@@ -194,6 +204,14 @@
194 box-sizing: border-box; 204 box-sizing: border-box;
195 } 205 }
196 206
207 video {
208 max-width: 100%;
209 }
210
211 iframe {
212 max-width: 100%;
213 }
214
197 figure { 215 figure {
198 background-color: #eee; 216 background-color: #eee;
199 border: 1px solid #bbb; 217 border: 1px solid #bbb;
@@ -201,6 +219,10 @@
201 font-size: 0.75em; 219 font-size: 0.75em;
202 line-height: 24px; 220 line-height: 24px;
203 221
222 @media only screen and (max-width: 767px) {
223 margin: 0;
224 }
225
204 img { 226 img {
205 display: block; 227 display: block;
206 border: 1px solid #bbb; 228 border: 1px solid #bbb;
@@ -450,3 +472,46 @@
450.next-post { 472.next-post {
451 float: right; 473 float: right;
452} 474}
475
476#streams-index {
477 width: 100%;
478 border-collapse: collapse;
479
480 #streams-index-header-row {
481 th {
482 background-color: #bdbdbd;
483 padding-left: 0.5em;
484 border-top: 1px solid #848484;
485 border-bottom: 1px solid #848484;
486 }
487 }
488
489 th {
490 padding-bottom: 0.5em;
491 padding-top: 0.5em;
492 text-align: left;
493 }
494
495 td {
496 padding-top: 1em;
497 padding-left: 0.5em;
498
499 time {
500 font-size: .75em;
501 }
502
503 .stream-link {
504 font-weight: bold;
505 text-decoration: none;
506
507 &, &:visited {
508 color: #ee2c2c;
509 }
510
511 &:hover {
512 text-decoration: underline;
513 color: #9ea1ad;
514 }
515 }
516 }
517}
diff --git a/app/assets/stylesheets/main/layout.scss b/app/assets/stylesheets/main/layout.scss index 5f5d0e6..ef31ada 100644 --- a/app/assets/stylesheets/main/layout.scss +++ b/app/assets/stylesheets/main/layout.scss
@@ -1,42 +1,47 @@
1body#main-body { 1body#main-body {
2 height: 100%; 2 height: 100%;
3 font-family: sans-serif; 3 font-family: sans-serif;
4 font-size: 16px;
4} 5}
5 6
6#container { 7#container {
7 height: 100%; 8 height: 100%;
8 margin: 10px auto;
9 width: 75%; /* 66% */
10 box-sizing: border-box; 9 box-sizing: border-box;
11 min-width: 910px; 10
11 @media only screen and (min-width: 768px) {
12 margin: 10px auto;
13 width: 75%; /* 66% */
14 min-width: 910px;
15 }
12} 16}
13 17
14#banner { 18#banner {
15 background-image: image-url("fourisland_header.png");
16 width: 900px;
17 height: 200px;
18 margin: 0 auto; 19 margin: 0 auto;
19 20
20 h1 { 21 h1 {
21 margin: 0; 22 display: none;
23 }
22 24
23 a { 25 img {
24 display: block; 26 width: 100%;
25 width: 900px; 27 max-width: 900px;
26 height: 200px; 28 display: block;
27 text-indent: -5000px; 29 margin: 0 auto;
28 text-decoration: none;
29 margin: 0;
30 }
31 } 30 }
32} 31}
33 32
34#page-body { 33#page-body {
35 display: flex; 34 display: flex;
35
36 @media only screen and (max-width: 767px) {
37 flex-direction: column-reverse;
38 }
36} 39}
37 40
38#main { 41#main {
39 width: 70%; 42 @media only screen and (min-width: 768px) {
43 width: 70%;
44 }
40} 45}
41 46
42#content { 47#content {
@@ -48,7 +53,8 @@ body#main-body {
48 width: 100%; 53 width: 100%;
49 text-align: center; 54 text-align: center;
50 padding: .25em; 55 padding: .25em;
51 font-weight: bold 56 font-weight: bold;
57 box-sizing: border-box;
52} 58}
53 59
54.flash-notice { 60.flash-notice {
@@ -61,15 +67,22 @@ body#main-body {
61 67
62#sidebar { 68#sidebar {
63 box-sizing: border-box; 69 box-sizing: border-box;
64 width: 30%;
65 font-size: .75em; 70 font-size: .75em;
66 color: black; 71 color: black;
67 margin-top: 1em; 72 margin-top: 1em;
68 margin-bottom: 2em; 73 margin-bottom: 2em;
69 font-family: sans-serif; 74 font-family: sans-serif;
75
76 @media only screen and (max-width: 767px) {
77 width: 100%;
78 }
79
80 @media only screen and (min-width: 768px) {
81 width: 30%;
82 }
70} 83}
71 84
72#sidebar h2 { 85#sidebar h2, #mobile-header h2 {
73 font-size: 1em; 86 font-size: 1em;
74} 87}
75 88
diff --git a/app/controllers/blogs_controller.rb b/app/controllers/blogs_controller.rb index 033d6bb..6e80754 100644 --- a/app/controllers/blogs_controller.rb +++ b/app/controllers/blogs_controller.rb
@@ -1,6 +1,7 @@
1require 'redcarpet/render_strip' 1
2 2
3class BlogsController < ApplicationController 3class BlogsController < ApplicationController
4 include ApplicationHelper
4 5
5 def summary 6 def summary
6 @blogs = Blog.where(published: true).order(published_at: :desc).paginate(page: params[:page], per_page: 10) 7 @blogs = Blog.where(published: true).order(published_at: :desc).paginate(page: params[:page], per_page: 10)
@@ -27,12 +28,12 @@ class BlogsController < ApplicationController
27 @prev = @blog.prev 28 @prev = @blog.prev
28 @next = @blog.next 29 @next = @blog.next
29 30
30 body = Redcarpet::Markdown.new(Redcarpet::Render::StripDown).render(@blog.body) 31 body = stripped_markdown(@blog.body)
31 32
32 set_meta_tags(og: { 33 set_meta_tags(og: {
33 title: @blog.title, 34 title: @blog.title,
34 type: "article", 35 type: "article",
35 description: (body.length <= 300 ? body : body[0..299]), 36 description: body[0, 299],
36 url: blog_url(@blog, host: "www.fourisland.com"), 37 url: blog_url(@blog, host: "www.fourisland.com"),
37 article: { 38 article: {
38 published_time: @blog.published_at.iso8601, 39 published_time: @blog.published_at.iso8601,
diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index b305d0a..31fe411 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb
@@ -5,6 +5,12 @@ class CommentsController < ApplicationController
5 raise ActiveRecord::RecordNotFound unless @blog 5 raise ActiveRecord::RecordNotFound unless @blog
6 raise ActiveRecord::RecordNotFound unless @blog.published 6 raise ActiveRecord::RecordNotFound unless @blog.published
7 7
8 unless verify_recaptcha
9 flash.alert = "Error posting comment."
10 render "blogs/show"
11 return
12 end
13
8 @comment = @blog.comments.new(comment_params) 14 @comment = @blog.comments.new(comment_params)
9 @comment.request_ip = request.ip 15 @comment.request_ip = request.ip
10 @comment.user_agent = request.user_agent 16 @comment.user_agent = request.user_agent
@@ -56,7 +62,8 @@ class CommentsController < ApplicationController
56 CommentMailer.with(comment: @comment).reply_comment_email.deliver_later 62 CommentMailer.with(comment: @comment).reply_comment_email.deliver_later
57 end 63 end
58 else 64 else
59 CommentMailer.with(comment: @comment).new_pending_comment_email.deliver_later 65 # CommentMailer.with(comment: @comment).new_pending_comment_email.deliver_later
66 # I'm disabling pending comment emails, at least for now, because I am getting too many.
60 end 67 end
61 else 68 else
62 flash.alert = "Error posting comment." 69 flash.alert = "Error posting comment."
diff --git a/app/controllers/quotes_controller.rb b/app/controllers/quotes_controller.rb index d4800cb..468f6ea 100644 --- a/app/controllers/quotes_controller.rb +++ b/app/controllers/quotes_controller.rb
@@ -30,6 +30,20 @@ class QuotesController < ApplicationController
30 end 30 end
31 end 31 end
32 32
33 def search_form
34 @q = Quote.published.ransack
35 end
36
37 def search
38 @quotes = Quote.published.ransack(params[:q]).result(distinct: true).paginate(page: params[:page], per_page: 10)
39
40 respond_to do |format|
41 format.html { render :list }
42 format.json { render :json => @quotes }
43 format.xml { render :xml => @quotes }
44 end
45 end
46
33 def random 47 def random
34 picked = Quote.where(state: :published).ids.sample 48 picked = Quote.where(state: :published).ids.sample
35 redirect_to quote_url(picked) 49 redirect_to quote_url(picked)
diff --git a/app/controllers/streams_controller.rb b/app/controllers/streams_controller.rb index 664f533..ec4cee8 100644 --- a/app/controllers/streams_controller.rb +++ b/app/controllers/streams_controller.rb
@@ -1,7 +1,26 @@
1class StreamsController < ApplicationController 1class StreamsController < ApplicationController
2 include ApplicationHelper
3
4 def index
5 @streams = Stream.order(latest_post_at: :desc).paginate(page: params[:page], per_page: 10)
6 end
2 7
3 def show 8 def show
4 @stream = Stream.find_by_slug(params[:slug]) 9 @stream = Stream.find_by_slug(params[:slug])
10 @updates = @stream.updates.paginate(page: params[:page], per_page: 10)
11
12 body = stripped_markdown(@stream.body)
13
14 set_meta_tags(og: {
15 title: @stream.title,
16 type: "article",
17 description: body[0, 299],
18 url: stream_url(@stream, host: "www.fourisland.com"),
19 article: {
20 published_time: @stream.created_at.iso8601,
21 modified_time: @stream.latest_post_at.iso8601
22 }
23 })
5 end 24 end
6 25
7end 26end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 72d7ab1..a51f1a1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb
@@ -1,6 +1,7 @@
1require 'redcarpet' 1require 'redcarpet'
2require 'rouge' 2require 'rouge'
3require 'rouge/plugins/redcarpet' 3require 'rouge/plugins/redcarpet'
4require 'redcarpet/render_strip'
4 5
5module ApplicationHelper 6module ApplicationHelper
6 7
@@ -22,7 +23,7 @@ module ApplicationHelper
22 def markdown(text, params = {}) 23 def markdown(text, params = {})
23 options = { fenced_code_blocks: true, highlight: true, footnotes: true } 24 options = { fenced_code_blocks: true, highlight: true, footnotes: true }
24 25
25 html_options = {} 26 html_options = { with_toc_data: true }
26 if params[:restricted] 27 if params[:restricted]
27 html_options[:filter_html] = true 28 html_options[:filter_html] = true
28 end 29 end
@@ -39,4 +40,20 @@ module ApplicationHelper
39 ] 40 ]
40 end 41 end
41 42
43 def friends_sidebar
44 [
45 { title: "Entropically", url: "https://entropically.neocities.org/" },
46 ]
47 end
48
49 class StrippedSummary < Redcarpet::Render::StripDown
50 def block_html(raw_html)
51 nil
52 end
53 end
54
55 def stripped_markdown(text)
56 Redcarpet::Markdown.new(StrippedSummary).render(text)
57 end
58
42end 59end
diff --git a/app/models/blog.rb b/app/models/blog.rb index db05432..03c0619 100644 --- a/app/models/blog.rb +++ b/app/models/blog.rb
@@ -15,7 +15,7 @@ class Blog < ApplicationRecord
15 validates :body, presence: true, if: :published 15 validates :body, presence: true, if: :published
16 validates :slug, presence: true, format: /\A[-a-z0-9]+\z/, if: :published 16 validates :slug, presence: true, format: /\A[-a-z0-9]+\z/, if: :published
17 validates :user, presence: true 17 validates :user, presence: true
18 validates :images, content_type: ['image/png', 'image/jpeg'] 18 validates :images, content_type: ['image/png', 'image/jpeg', 'video/mp4', 'image/gif']
19 19
20 before_validation :set_draft_title 20 before_validation :set_draft_title
21 before_save :set_published_at 21 before_save :set_published_at
diff --git a/app/models/quote.rb b/app/models/quote.rb index 3301667..b1d25b4 100644 --- a/app/models/quote.rb +++ b/app/models/quote.rb
@@ -36,4 +36,13 @@ class Quote < ApplicationRecord
36 def has_tags? 36 def has_tags?
37 !tags.empty? 37 !tags.empty?
38 end 38 end
39
40 def self.ransackable_attributes(auth_object = nil)
41 ["content", "notes"]
42 end
43
44 def self.ransackable_associations(auth_object = nil)
45 []
46 end
47
39end 48end
diff --git a/app/models/stream.rb b/app/models/stream.rb index 0773143..6a738e2 100644 --- a/app/models/stream.rb +++ b/app/models/stream.rb
@@ -8,11 +8,22 @@ class Stream < ApplicationRecord
8 validates :title, presence: true 8 validates :title, presence: true
9 validates :slug, presence: true, format: /\A[-a-z0-9]+\z/ 9 validates :slug, presence: true, format: /\A[-a-z0-9]+\z/
10 10
11 before_create :set_post_timestamp
12
11 def path 13 def path
12 "/thinks/#{slug}" 14 "/thinks/#{slug}"
13 end 15 end
14 16
17 def to_param
18 slug
19 end
20
15 def taggable 21 def taggable
16 self 22 self
17 end 23 end
24
25 private
26 def set_post_timestamp
27 self.latest_post_at = self.created_at
28 end
18end 29end
diff --git a/app/models/update.rb b/app/models/update.rb index 01907d8..a98a5d4 100644 --- a/app/models/update.rb +++ b/app/models/update.rb
@@ -5,6 +5,8 @@ class Update < ApplicationRecord
5 5
6 validates :stream, :body, presence: true 6 validates :stream, :body, presence: true
7 7
8 after_create :set_latest_timestamp
9
8 def path 10 def path
9 "/thinks/#{stream.slug}\#update-#{id}" 11 "/thinks/#{stream.slug}\#update-#{id}"
10 end 12 end
@@ -12,4 +14,10 @@ class Update < ApplicationRecord
12 def taggable 14 def taggable
13 stream 15 stream
14 end 16 end
17
18 private
19 def set_latest_timestamp
20 self.stream.latest_post_at = self.created_at
21 self.stream.save!
22 end
15end 23end
diff --git a/app/views/admin/blogs/_form.html.haml b/app/views/admin/blogs/_form.html.haml index f64ebe5..323d499 100644 --- a/app/views/admin/blogs/_form.html.haml +++ b/app/views/admin/blogs/_form.html.haml
@@ -42,9 +42,13 @@
42 .details-module.image-uploads 42 .details-module.image-uploads
43 %h4 Images 43 %h4 Images
44 - f.object.images.each do |image| 44 - f.object.images.each do |image|
45 = image_tag image.variant(:thumb) 45 - if image.representable?
46 = f.hidden_field :images, multiple: true, value: image.signed_id 46 = image_tag image.variant(:thumb)
47 %pre= rails_blob_url(image) 47 - else
48 = image.filename.to_s
49 - unless image.new_record?
50 = f.hidden_field :images, multiple: true, value: image.signed_id
51 %pre= rails_blob_url(image)
48 %hr 52 %hr
49 = f.file_field :images, multiple: true 53 = f.file_field :images, multiple: true
50 .details-module= f.submit 54 .details-module= f.submit
diff --git a/app/views/admin/streams/edit.html.haml b/app/views/admin/streams/edit.html.haml index 8d910fe..8c250e2 100644 --- a/app/views/admin/streams/edit.html.haml +++ b/app/views/admin/streams/edit.html.haml
@@ -1,3 +1,3 @@
1- title "Editing #{@stream.title}" 1- title "Editing #{@stream.title}"
2= form_for @stream, url: admin_stream_url(@stream), html: { id: "entry-form" } do |f| 2= form_for @stream, url: admin_stream_url(@stream.id), html: { id: "entry-form" } do |f|
3 = render partial: "form", locals: { f: f } 3 = render partial: "form", locals: { f: f }
diff --git a/app/views/admin/streams/index.html.haml b/app/views/admin/streams/index.html.haml index 6903fd1..a2a25d7 100644 --- a/app/views/admin/streams/index.html.haml +++ b/app/views/admin/streams/index.html.haml
@@ -10,5 +10,5 @@
10 %td= stream.created_at.strftime("%B %d, %Y, %l:%M%P") 10 %td= stream.created_at.strftime("%B %d, %Y, %l:%M%P")
11 %td 11 %td
12 %ul.admin-actions 12 %ul.admin-actions
13 %li= link_to "Edit", edit_admin_stream_url(stream) 13 %li= link_to "Edit", edit_admin_stream_url(stream.id)
14 %li= link_to "Add Update", new_admin_stream_update_url(stream) 14 %li= link_to "Add Update", new_admin_stream_update_url(stream.id)
diff --git a/app/views/admin/updates/edit.html.haml b/app/views/admin/updates/edit.html.haml index 75a9957..3cab0b6 100644 --- a/app/views/admin/updates/edit.html.haml +++ b/app/views/admin/updates/edit.html.haml
@@ -1,3 +1,3 @@
1- title "Editing stream update" 1- title "Editing stream update"
2= form_for @update, url: admin_stream_update_url(@stream, @update), html: { id: "entry-form" } do |f| 2= form_for @update, url: admin_stream_update_url(@stream.id, @update), html: { id: "entry-form" } do |f|
3 = render partial: "form", locals: { f: f } 3 = render partial: "form", locals: { f: f }
diff --git a/app/views/comments/_form.html.haml b/app/views/comments/_form.html.haml index 13a0903..12692c2 100644 --- a/app/views/comments/_form.html.haml +++ b/app/views/comments/_form.html.haml
@@ -24,6 +24,9 @@
24 .comment-website-field.comment-field 24 .comment-website-field.comment-field
25 .comment-field-label= f.label :website, "Website (Optional)" 25 .comment-field-label= f.label :website, "Website (Optional)"
26 .comment-field-input= f.text_field :website, type: :url 26 .comment-field-input= f.text_field :website, type: :url
27 .comment-website-field.comment-field
28 .comment-field-label
29 .comment-field-input= recaptcha_tags
27 .comment-submit-button.comment-field 30 .comment-submit-button.comment-field
28 .comment-field-label 31 .comment-field-label
29 .comment-field-input= f.submit "Post" 32 .comment-field-input= f.submit "Post"
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 90ea5e2..1d0bea3 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml
@@ -9,15 +9,16 @@
9 %link{rel: "me", href: "mailto:fefferburbia+thoughts@gmail.com"} 9 %link{rel: "me", href: "mailto:fefferburbia+thoughts@gmail.com"}
10 %link{rel: "webmention", href: webmentions_url} 10 %link{rel: "webmention", href: webmentions_url}
11 %link{rel: "alternate", type: "application/atom+xml", href: blogs_url(format: :atom)} 11 %link{rel: "alternate", type: "application/atom+xml", href: blogs_url(format: :atom)}
12 %meta{name: "viewport", content: "width=device-width, initial-scale=1.0"}
12 %body#main-body 13 %body#main-body
13 - if flash[:alert] 14 - if flash[:alert]
14 %div#flash.flash-alert= flash[:alert] 15 %div#flash.flash-alert= flash[:alert]
15 - if flash[:notice] 16 - if flash[:notice]
16 %div#flash.flash-notice= flash[:notice] 17 %div#flash.flash-notice= flash[:notice]
17 #container 18 #container
18 #page-header 19 %header#banner
19 %header#banner 20 %h1 Four Island
20 %h1= link_to "Four Island", root_url 21 = link_to image_tag("fourisland_header.png"), root_url
21 #page-body 22 #page-body
22 #sidebar 23 #sidebar
23 .sidebar-module 24 .sidebar-module
@@ -53,6 +54,14 @@
53 = link_to link[:title], link[:url], link.include?(:extra) ? link[:extra] : {} 54 = link_to link[:title], link[:url], link.include?(:extra) ? link[:extra] : {}
54 .sidebar-module.fun-links 55 .sidebar-module.fun-links
55 .bubble.rounded 56 .bubble.rounded
57 %h2 Friends
58 %ul
59 - friends_sidebar().each do |link|
60 %li
61 = image_tag "tag_#{cycle("blue", "green", "orange", "pink", "purple", "red", "yellow")}.png"
62 = link_to link[:title], link[:url], link.include?(:extra) ? link[:extra] : {}
63 .sidebar-module.fun-links
64 .bubble.rounded
56 %h2 Blog links 65 %h2 Blog links
57 %ul 66 %ul
58 %li 67 %li
diff --git a/app/views/layouts/quotes.html.haml b/app/views/layouts/quotes.html.haml index 4a7681e..4b21000 100644 --- a/app/views/layouts/quotes.html.haml +++ b/app/views/layouts/quotes.html.haml
@@ -25,6 +25,7 @@
25 - if user_signed_in? 25 - if user_signed_in?
26 %li= link_to_unless_current "Submit", new_quote_url 26 %li= link_to_unless_current "Submit", new_quote_url
27 %li= link_to_unless_current "Tags", tags_quotes_url 27 %li= link_to_unless_current "Tags", tags_quotes_url
28 %li= link_to_unless_current "Search", search_form_quotes_url
28 %li= link_to_unless_current "Stats", stats_quotes_url 29 %li= link_to_unless_current "Stats", stats_quotes_url
29 %li= link_to_unless_current "Feed", latest_quotes_url(:atom) 30 %li= link_to_unless_current "Feed", latest_quotes_url(:atom)
30 - if user_signed_in? 31 - if user_signed_in?
diff --git a/app/views/quotes/search_form.html.haml b/app/views/quotes/search_form.html.haml new file mode 100644 index 0000000..74cdd20 --- /dev/null +++ b/app/views/quotes/search_form.html.haml
@@ -0,0 +1,6 @@
1%h3 Search
2= search_form_for @q, url: search_quotes_url do |f|
3 .form-field
4 = f.label :content_or_notes_cont, "Query:"
5 = f.search_field :content_or_notes_cont
6 .form-field= f.submit
diff --git a/app/views/streams/_stream.html.haml b/app/views/streams/_stream.html.haml index 84a6478..97d4813 100644 --- a/app/views/streams/_stream.html.haml +++ b/app/views/streams/_stream.html.haml
@@ -1,6 +1,8 @@
1%article#stream-post 1%article#stream-post
2 %h2#stream-title= stream.title 2 %h2#stream-title= stream.title
3 - unless stream.body.blank? 3 - unless stream.body.blank?
4 %header#stream-intro.entry-content= stream.body.html_safe 4 %header#stream-intro.entry-content= markdown(stream.body)
5 - unless stream.updates.empty? 5 = will_paginate @updates
6 = render stream.updates 6 - unless @updates.empty?
7 = render @updates
8 = will_paginate @updates
diff --git a/app/views/streams/index.html.haml b/app/views/streams/index.html.haml new file mode 100644 index 0000000..bf5073f --- /dev/null +++ b/app/views/streams/index.html.haml
@@ -0,0 +1,19 @@
1%table#streams-index
2 %tr#streams-index-header-row
3 %th Stream Topic
4 %th Updates
5 %th Last Post
6 - @streams.each do |stream|
7 %tr
8 %td
9 = link_to stream.title, stream, class: "stream-link"
10 %br
11 %time= stream.created_at.strftime("%B #{stream.created_at.day.ordinalize}, %Y at %-I:%M:%S%P")
12 %td= stream.updates.size
13 %td
14 - unless stream.updates.empty?
15 - latest_post = stream.updates.order(created_at: :desc).first
16 = link_to (stripped_markdown(latest_post.body)[0, 30] + "..."), stream
17 %br
18 %time= latest_post.created_at.strftime("%B #{latest_post.created_at.day.ordinalize}, %Y at %-I:%M:%S%P")
19= will_paginate @streams
diff --git a/app/views/streams/show.html.haml b/app/views/streams/show.html.haml index 8365556..8d58f62 100644 --- a/app/views/streams/show.html.haml +++ b/app/views/streams/show.html.haml
@@ -1,3 +1,3 @@
1- title @stream.title 1- title @stream.title
2.breadcrumb= link_to "← Back to home page", root_path 2.breadcrumb= link_to "← Back to streams", streams_path
3= render @stream 3= render @stream
diff --git a/app/views/updates/_update.html.haml b/app/views/updates/_update.html.haml index 57f4158..ceb7d8f 100644 --- a/app/views/updates/_update.html.haml +++ b/app/views/updates/_update.html.haml
@@ -1,3 +1,6 @@
1%section.stream-update.entry-content{ id: "update-#{update.id}" } 1%section.stream-update.entry-content{ id: "update-#{update.id}" }
2 %time.update-posted= update.created_at.strftime("%B #{update.created_at.day.ordinalize}, %Y at %-I:%M:%S%P") 2 %header.update-posted
3 = update.body.html_safe 3 %time= update.created_at.strftime("%B #{update.created_at.day.ordinalize}, %Y at %-I:%M:%S%P")
4 - if user_signed_in? and !update.new_record?
5 = link_to "Edit", edit_admin_stream_update_path(update.stream.id, update), :class => "update-edit-link"
6 = markdown(update.body)
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index 90d7ee5..cd9cac5 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc
@@ -1 +1 @@
KrLA+ftWWLuC9JQBmExf5RXFOqp9q+CZCY6DJ8d0sgVUQXxcDToVqntxRRsqrTHvJ48xmRUgA4OmXUI1ZE3JtJQOuRmQHJf3bMtSlXDOzQTgNeIyzH3++Kcss3dIeM2+gnT8wxE2y6Tj2+KyB4tJGMeX1elSFK41rGz4zQzYVva3MVNKUmD1wV9p28sxtUSkDbiFxa2tDLGddor3Nqf7n4nuBLxpU4G1b7lhsAAQ4MYALvll0q3Uk54vEqyoiWEDC0fzqi1eWH/MQjcY6h6tXKEgElrXekx8N3agScXS50n2OzGx2ZbW99AwD0DxY6v1PtlAuW2OwEPFPvYaTu5cfsfaAJ5NKEMN4ahkHAerZpuJuoYgPOmnoM+mUZVPkaUWTRFkPBFRomrXtZ6e6OTAi/6Tnq9r/PAgpK9xeK0t8B2g0EpY/1FGvoByC0GDrzF2iJZg79y7B5xqhP9APrkTSJLCiNB3IdWwc2n45U6lPqt8ew1s9uxHQ5M9olBOkXnTDsj2--XBeKpE20qGij/dTL--9IG+AQxUdaR/ZcaUQ/cRvQ== \ No newline at end of file TMbFARKxiuopoIWE/n0a8KMCh28dFjTGFpfXoXHUQmDlVhA40doz/aLWCYDE00djC70eM4BwXZmaDFC7tly1miU0OymvYijYKNb5Kv9bu2rihSprleyjnBrCF4qykWpDh8XfzXGfiDegSuZzw5RUGKkEU+EGJNIPTnPKAm2MSSnPStVFt1gGxGuYRd0pXCgFUW/USVYnbJEPv6DOxqh3HmKmneP29fcx1V+N/RccyzHsjL2S5lej5s8Q4oAy+SZ4+xLK9Pdh6nA3dZ82fNTLffqmnFxEovuW5n6Cjt0Ka2MZRvYg6sBNDvUVvQSw4u1BRXx7JOHcIWiKdykF/uHdrSwmIPepwd+zs/HCxrthdMSry+yrLwJpZ67JiMA/VKIMYzgLy8yk2JEblBXq3FGo44TWwumIkfTn4H/6UZNb4jGJ4dH+LAFiE7il2X3BbvBb4EkYB3xbu+zqvnohOzuVKkJtjILV6RBrEFzbCijwi3PBOoX8O9fwW8B0BduSp4AhFa+dyenOkD2HjtyEMttJAPuuw3neQKhkv/4SaPaugcD4vKONSwU3F/7nB7yMVQqofVLIc2a1tJaTjWIlNIwVssZS2HS5OZFTpv1XJpCyMcLO/EAe9poqiPieE8WwpdXntRs9O/QQqPWyt2ZdHD8RMKerZjbU1wV58h4rJmmAKUkTCuo=--Jo9AwrM/hr9nWWoS--iHpkNsZwBWB0lySmzkcLdA== \ No newline at end of file
diff --git a/config/credentials/production.yml.enc b/config/credentials/production.yml.enc index a4724d9..af415a7 100644 --- a/config/credentials/production.yml.enc +++ b/config/credentials/production.yml.enc
@@ -1 +1 @@
d4uIaJsejFxp80UN6tMjOdA63ERQWbOxbgHuo+FyTRTHXAJbriKgcx2GLhMFOjk7ft8IZm0Nv5/e6NOzaqTYRiwNMG+X4jaX267pIAccMYBDoZmIhfQGqkav8bEMvA3PODeoaEVHTEP1dOFtI5u2rGi2EUYRThPXLzdOpoOjFAnC8cQawiFJn6B9EpYu5zlAfCJ9LonCVPCaOlWqzjd+8RbgqLvtw7fNBlIX8u1cli8JwBiV8C1UNlqmv6b1Lqp1S20PizOnUA2GU2Pi2EWEtjavO82MCakS2I0rt90Cikd9mlp+LOcWpk3AgVr3dhi86XepTk5Ds3v6g7axzvTxUZjV9ACXhSHufPlDvqVqAcwpEE5C/o7WHj3T3xy4kVs36LScMcyQ/bg85wYxgT+Wl0EVSMg7DPsl/pY/QkttizStx++r8x6sExcPxD5n8ZLA5aOXjcfeYJC+BYg+7OWlPYHqup8s/70gjm8hZ75Rv+yPD7PEz6MIJu+pG2MUfgJspmYag0Wc2hrHxHnMi4S+Jf+kK+7x44nVXPJ5kT8WXYVQkHdj6ibgwPqe4OWbr0dWkoowIcyztmdj+nKRulzwgYNgyd23XZqu30TTuyqVtzzyVfXLteGDSDS4kmXyn/hx2LtG6LHzAeptsIChPJOCu9+1Zp4M9haCSw8mX/d8/nAt5MyxZUm/2SXogD2dE4eNbGhMFeDJkeJ/--T1kw8AC3IoFq+eQx--2bqSYqOxBpLVMJnddXLegQ== \ No newline at end of file V//BL1LV+HIfxWVXgfAHG5TrUakYlbjN5V3gkDi9StysvSQ0rM3Opo2/Vt0KbU4FHhjiHoGAOZRPdf08j5k3zS3i4YroBfR3HwOj7snvq2mdBleL9D1UqkBLldjdaco1MnSLSfS2yJe8F3VhtVxesz/Sh4LXoZWemsWGJLu0GfRxPDwswTvdWBgyASnKUOlFyQNypgRTWGMqCe99Z5pw8co8JqZho29Dk6Tp7UJzoim8j5YU+tfgBZ4GF81c0eT8SvBJhHHbcTm/DPUMGxvUJAT9adjQnjPq0tferHziTGl54/ROQCXqrJHKLUOH5JRqh2lAkY3EK3SvTezuYSoV0MwsOxntxC54bQYznN5Dz6/I31UbWpydPGY8P+/Sjdh4QInD51VcDeuJ/QaDSeqJFxHUWDJ0Da55rwsZFr6jmpgbqcmunXLl2B+VDH7sSQOn71IEyNikq5l/9m2dYEdXZpWFsujkyVXzg4O5WFkLtmqoe4wudN7kk8nEZffd6xPtKNpl51Ml1BpSoNMNwGAf72Xxq5/5Z9QFK7mtyPhBVYqalR0kEZPbxmeZGmedcE/fgQXlZYNw4tzBy2/24hJokMLRoX5prmOGMf+6f62wEbBozyaDU2eAMC0HCwg/rb22hDPvzs1XKu2TTEGK7iT3BvdsUHIVnHdUd//hLEeNHeI43FLxPKgyaG7Ovnn8DmNrvUuapaFHjykVsQ2GOLQ9a7GGrwObqr0w33HG/UAwhzEE8s9KwF6qMdqPCCPuYOsbj5xgTyv5X5s0oIgCinvkfAIyFV7WXwLbbE9yxc6UumXbLLTSQE2s8mZ2yL+gdE+iYkSOhz8Z3bj9agOQD5zHMu/NxzTdpNwH6rsLLXq5EfToIWNXEl9kTU8=--x7Ic40D296IOMLm6--VBDaN0LLlyO8zeyfWW+UBA== \ No newline at end of file
diff --git a/config/deploy.rb b/config/deploy.rb index f28894f..de297e3 100644 --- a/config/deploy.rb +++ b/config/deploy.rb
@@ -1,5 +1,5 @@
1# config valid only for current version of Capistrano 1# config valid only for current version of Capistrano
2lock "3.18.0" 2lock "3.19.2"
3 3
4set :application, "thoughts" 4set :application, "thoughts"
5set :repo_url, "/srv/git/thoughts" 5set :repo_url, "/srv/git/thoughts"
diff --git a/config/initializers/recaptcha.rb b/config/initializers/recaptcha.rb new file mode 100644 index 0000000..807a23c --- /dev/null +++ b/config/initializers/recaptcha.rb
@@ -0,0 +1,4 @@
1Recaptcha.configure do |config|
2 config.site_key = Rails.application.credentials.recaptcha_site_key
3 config.secret_key = Rails.application.credentials.recaptcha_secret_key
4end
diff --git a/config/routes.rb b/config/routes.rb index 76378c4..ad66501 100644 --- a/config/routes.rb +++ b/config/routes.rb
@@ -56,6 +56,7 @@ Rails.application.routes.draw do
56 end 56 end
57 end 57 end
58 58
59 get 'thinks', to: 'streams#index', as: :streams
59 get 'thinks/:slug', to: 'streams#show', as: :stream 60 get 'thinks/:slug', to: 'streams#show', as: :stream
60 61
61 get 'plays', to: 'games#index' 62 get 'plays', to: 'games#index'
@@ -74,6 +75,8 @@ Rails.application.routes.draw do
74 get 'tags' 75 get 'tags'
75 get 'tags/:id', :action => "tag", :as => "tag" 76 get 'tags/:id', :action => "tag", :as => "tag"
76 get 'stats' 77 get 'stats'
78 get 'search_form'
79 get 'search'
77 end 80 end
78 81
79 member do 82 member do
diff --git a/db/migrate/20250112025207_widen_blog_body_column.rb b/db/migrate/20250112025207_widen_blog_body_column.rb new file mode 100644 index 0000000..f65aaea --- /dev/null +++ b/db/migrate/20250112025207_widen_blog_body_column.rb
@@ -0,0 +1,5 @@
1class WidenBlogBodyColumn < ActiveRecord::Migration[7.1]
2 def change
3 change_column :blogs, :body, :text, limit: 16.megabytes - 1
4 end
5end
diff --git a/db/migrate/20250512181245_add_latest_post_at_to_stream.rb b/db/migrate/20250512181245_add_latest_post_at_to_stream.rb new file mode 100644 index 0000000..9d753e3 --- /dev/null +++ b/db/migrate/20250512181245_add_latest_post_at_to_stream.rb
@@ -0,0 +1,21 @@
1class AddLatestPostAtToStream < ActiveRecord::Migration[7.1]
2 def up
3 add_column :streams, :latest_post_at, :datetime
4
5 Stream.all.each do |stream|
6 if stream.updates.empty?
7 stream.latest_post_at = stream.created_at
8 else
9 stream.latest_post_at = stream.updates.order(created_at: :desc).first.created_at
10 end
11
12 stream.save!
13 end
14
15 change_column_null :streams, :latest_post_at, false
16 end
17
18 def down
19 remove_column :streams, :latest_post_at, :datetime
20 end
21end
diff --git a/db/schema.rb b/db/schema.rb index b01ed43..76e9ad8 100644 --- a/db/schema.rb +++ b/db/schema.rb
@@ -10,7 +10,7 @@
10# 10#
11# It's strongly recommended that you check this file into your version control system. 11# It's strongly recommended that you check this file into your version control system.
12 12
13ActiveRecord::Schema[7.1].define(version: 2024_12_07_200746) do 13ActiveRecord::Schema[7.1].define(version: 2025_05_12_181245) do
14 create_table "active_storage_attachments", force: :cascade do |t| 14 create_table "active_storage_attachments", force: :cascade do |t|
15 t.string "name", null: false 15 t.string "name", null: false
16 t.string "record_type", null: false 16 t.string "record_type", null: false
@@ -63,7 +63,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_12_07_200746) do
63 63
64 create_table "blogs", force: :cascade do |t| 64 create_table "blogs", force: :cascade do |t|
65 t.string "title" 65 t.string "title"
66 t.text "body" 66 t.text "body", limit: 16777215
67 t.string "slug" 67 t.string "slug"
68 t.datetime "created_at", precision: nil, null: false 68 t.datetime "created_at", precision: nil, null: false
69 t.datetime "updated_at", precision: nil, null: false 69 t.datetime "updated_at", precision: nil, null: false
@@ -358,6 +358,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_12_07_200746) do
358 t.string "slug" 358 t.string "slug"
359 t.datetime "created_at", precision: nil, null: false 359 t.datetime "created_at", precision: nil, null: false
360 t.datetime "updated_at", precision: nil, null: false 360 t.datetime "updated_at", precision: nil, null: false
361 t.datetime "latest_post_at", null: false
361 end 362 end
362 363
363 create_table "taggings", force: :cascade do |t| 364 create_table "taggings", force: :cascade do |t|