diff options
24 files changed, 587 insertions, 2 deletions
| diff --git a/Gemfile b/Gemfile index 887f7eb..fd377fe 100644 --- a/Gemfile +++ b/Gemfile | |||
| @@ -83,3 +83,4 @@ gem 'akismet' | |||
| 83 | gem 'active_storage_validations' | 83 | gem 'active_storage_validations' |
| 84 | gem "image_processing", ">= 1.2" | 84 | gem "image_processing", ">= 1.2" |
| 85 | gem "meta-tags" | 85 | gem "meta-tags" |
| 86 | gem 'rails_autolink' | ||
| diff --git a/Gemfile.lock b/Gemfile.lock index 18f75fe..07908ec 100644 --- a/Gemfile.lock +++ b/Gemfile.lock | |||
| @@ -264,6 +264,10 @@ GEM | |||
| 264 | rails-html-sanitizer (1.6.0) | 264 | rails-html-sanitizer (1.6.0) |
| 265 | loofah (~> 2.21) | 265 | loofah (~> 2.21) |
| 266 | nokogiri (~> 1.14) | 266 | nokogiri (~> 1.14) |
| 267 | rails_autolink (1.1.8) | ||
| 268 | actionview (> 3.1) | ||
| 269 | activesupport (> 3.1) | ||
| 270 | railties (> 3.1) | ||
| 267 | railties (7.0.8) | 271 | railties (7.0.8) |
| 268 | actionpack (= 7.0.8) | 272 | actionpack (= 7.0.8) |
| 269 | activesupport (= 7.0.8) | 273 | activesupport (= 7.0.8) |
| @@ -382,6 +386,7 @@ DEPENDENCIES | |||
| 382 | paperclip | 386 | paperclip |
| 383 | pokeviewer! | 387 | pokeviewer! |
| 384 | rails (~> 7.0.3) | 388 | rails (~> 7.0.3) |
| 389 | rails_autolink | ||
| 385 | redcarpet | 390 | redcarpet |
| 386 | rouge | 391 | rouge |
| 387 | sassc-rails | 392 | sassc-rails |
| diff --git a/app/assets/stylesheets/quotes.css.scss b/app/assets/stylesheets/quotes.css.scss new file mode 100644 index 0000000..8cf4eae --- /dev/null +++ b/app/assets/stylesheets/quotes.css.scss | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | /* | ||
| 2 | *= require normalize-rails | ||
| 3 | *= require_tree ./quotes | ||
| 4 | */ | ||
| diff --git a/app/assets/stylesheets/quotes/layout.css.sass b/app/assets/stylesheets/quotes/layout.css.sass new file mode 100644 index 0000000..e5dbd64 --- /dev/null +++ b/app/assets/stylesheets/quotes/layout.css.sass | |||
| @@ -0,0 +1,215 @@ | |||
| 1 | body | ||
| 2 | font-family: "Century Gothic", sans-serif | ||
| 3 | background-color: rgb(102,102,102) | ||
| 4 | |||
| 5 | #wrap | ||
| 6 | width: 80% | ||
| 7 | margin: 1em auto | ||
| 8 | background-color: white | ||
| 9 | border: 1px solid black | ||
| 10 | |||
| 11 | #banner | ||
| 12 | background-color: rgb(153,0,0) | ||
| 13 | font-size: 1.25em | ||
| 14 | font-width: bold | ||
| 15 | padding: 6px | ||
| 16 | #banner-title | ||
| 17 | float: left | ||
| 18 | a | ||
| 19 | text-decoration: none | ||
| 20 | color: white | ||
| 21 | #banner-abbr | ||
| 22 | color: white | ||
| 23 | font-style: italic | ||
| 24 | float: right | ||
| 25 | |||
| 26 | #oneliner | ||
| 27 | padding: 4px | ||
| 28 | font-style: italic | ||
| 29 | background-color: rgb(240,240,240) | ||
| 30 | a | ||
| 31 | color: black | ||
| 32 | cite:before | ||
| 33 | content: " \2013" | ||
| 34 | |||
| 35 | #top-bar | ||
| 36 | background-color: black | ||
| 37 | a | ||
| 38 | text-decoration: none | ||
| 39 | &:hover | ||
| 40 | color: purple | ||
| 41 | ul | ||
| 42 | float: right | ||
| 43 | padding: 4px | ||
| 44 | li | ||
| 45 | margin-left: 1em | ||
| 46 | display: inline | ||
| 47 | list-style-type: none | ||
| 48 | a | ||
| 49 | color: white | ||
| 50 | font-weight: bold | ||
| 51 | &.selected a | ||
| 52 | color: yellow | ||
| 53 | |||
| 54 | .pagination | ||
| 55 | &+.quote | ||
| 56 | border-top: 1px solid black | ||
| 57 | |||
| 58 | #quotes | ||
| 59 | .quote | ||
| 60 | margin: 0 | ||
| 61 | padding-bottom: 0.1em | ||
| 62 | background-color: #fcfcfc | ||
| 63 | border-bottom: 1px solid black | ||
| 64 | .quote-header | ||
| 65 | background-color: #f7f7f7 | ||
| 66 | border: 1px solid #f4f4f4 | ||
| 67 | margin: 0 | ||
| 68 | padding: 0.25em 0.75em | ||
| 69 | * | ||
| 70 | margin: 0 0.125em | ||
| 71 | a | ||
| 72 | text-decoration: none | ||
| 73 | .vote-link | ||
| 74 | color: #888 | ||
| 75 | .quote-upvote-link | ||
| 76 | color: #090 | ||
| 77 | font-weight: bold | ||
| 78 | .quote-downvote-link | ||
| 79 | color: #900 | ||
| 80 | font-weight: bold | ||
| 81 | .quote-link | ||
| 82 | font-weight: bold | ||
| 83 | datetime | ||
| 84 | font-size: 80% | ||
| 85 | .quote-edit-link | ||
| 86 | font-size: 0.9em | ||
| 87 | float: right | ||
| 88 | blockquote | ||
| 89 | font-family: Consolas, Monaco, "Courier New", monospace | ||
| 90 | font-size: 14px | ||
| 91 | margin: 0.5em 0.75em | ||
| 92 | padding: 0 | ||
| 93 | position: static | ||
| 94 | width: 100% | ||
| 95 | &:hover | ||
| 96 | background-color: #fffcec | ||
| 97 | .quote-header | ||
| 98 | background-color: #fec | ||
| 99 | border-color: #ffe9c9 | ||
| 100 | .quote-footer | ||
| 101 | color: #666 | ||
| 102 | border-top: 1px dashed #EEE | ||
| 103 | margin: 0 0.75em | ||
| 104 | padding: 0.25em 0 | ||
| 105 | * | ||
| 106 | font-size: 0.9em | ||
| 107 | .quote-tags | ||
| 108 | margin: 0 | ||
| 109 | padding: 0 | ||
| 110 | list-style-type: none | ||
| 111 | li | ||
| 112 | display: inline | ||
| 113 | a | ||
| 114 | color: #666 | ||
| 115 | &:before | ||
| 116 | content: "TAGS:" | ||
| 117 | .quote-notes + .quote-tags | ||
| 118 | margin-top: 0.5em | ||
| 119 | |||
| 120 | #page-body | ||
| 121 | p.normal | ||
| 122 | margin: .5em | ||
| 123 | ul | ||
| 124 | margin: .5em .5em .5em 2em | ||
| 125 | h2 | ||
| 126 | margin: .25em 0 0 .5em | ||
| 127 | h3 | ||
| 128 | padding-bottom: 0.25em | ||
| 129 | border-bottom: 1px dashed gray | ||
| 130 | margin: .25em 0 0 .5em | ||
| 131 | .form-field | ||
| 132 | margin: 0.5em | ||
| 133 | textarea | ||
| 134 | width: 100% | ||
| 135 | #about-left | ||
| 136 | float: left | ||
| 137 | width: 48% | ||
| 138 | margin-right: 1em | ||
| 139 | #about-right | ||
| 140 | float: right | ||
| 141 | width: 48% | ||
| 142 | margin-left: 1em | ||
| 143 | margin-bottom: 1em | ||
| 144 | |||
| 145 | #error-messages | ||
| 146 | margin: .5em | ||
| 147 | border: 1px solid black | ||
| 148 | background-color: #FF4040 | ||
| 149 | padding: .5em | ||
| 150 | h4 | ||
| 151 | font-size: 1.25em | ||
| 152 | |||
| 153 | #flash | ||
| 154 | margin: .5em | ||
| 155 | border: 1px solid black | ||
| 156 | background-color: #FBEC5D | ||
| 157 | padding: .5em | ||
| 158 | h4 | ||
| 159 | font-size: 1.25em | ||
| 160 | |||
| 161 | #tags-container | ||
| 162 | margin: 1em | ||
| 163 | list-style-type: none | ||
| 164 | li | ||
| 165 | display: inline | ||
| 166 | margin-left: 1em | ||
| 167 | .css1 | ||
| 168 | font-size: 1.0em | ||
| 169 | .css1_5 | ||
| 170 | font-size: 1.1em | ||
| 171 | .css2 | ||
| 172 | font-size: 1.2em | ||
| 173 | .css2_5 | ||
| 174 | font-size: 1.3em | ||
| 175 | .css3 | ||
| 176 | font-size: 1.4em | ||
| 177 | .css3_5 | ||
| 178 | font-size: 1.5em | ||
| 179 | .css4 | ||
| 180 | font-size: 1.6em | ||
| 181 | .css4_5 | ||
| 182 | font-size: 1.7em | ||
| 183 | .css5 | ||
| 184 | font-size: 1.8em | ||
| 185 | .css5_5 | ||
| 186 | font-size: 1.9em | ||
| 187 | .css6 | ||
| 188 | font-size: 2.0em | ||
| 189 | |||
| 190 | #search-form | ||
| 191 | margin: 0.5em | ||
| 192 | input.searchform | ||
| 193 | width: 50% | ||
| 194 | |||
| 195 | .audioplayer | ||
| 196 | float: right | ||
| 197 | |||
| 198 | footer | ||
| 199 | padding: 4px | ||
| 200 | color: white | ||
| 201 | text-align: center | ||
| 202 | font-size: 0.75em | ||
| 203 | clear: both | ||
| 204 | font-weight: bold | ||
| 205 | padding-bottom: .5em | ||
| 206 | background-color: rgb(153,0,0) | ||
| 207 | #footer-left | ||
| 208 | float: left | ||
| 209 | #footer-right | ||
| 210 | float: right | ||
| 211 | a | ||
| 212 | color: white | ||
| 213 | |||
| 214 | .cleardiv | ||
| 215 | clear: both \ No newline at end of file | ||
| diff --git a/app/assets/stylesheets/quotes/pagination.css.scss b/app/assets/stylesheets/quotes/pagination.css.scss new file mode 100644 index 0000000..dad8b88 --- /dev/null +++ b/app/assets/stylesheets/quotes/pagination.css.scss | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | .pagination { | ||
| 2 | text-align: center; | ||
| 3 | padding: 0.3em; | ||
| 4 | cursor: default; | ||
| 5 | margin: 0.5em 0; | ||
| 6 | |||
| 7 | a, span, em { | ||
| 8 | padding: 0.2em 0.5em; | ||
| 9 | } | ||
| 10 | |||
| 11 | .disabled { | ||
| 12 | color: #aaaaaa; | ||
| 13 | } | ||
| 14 | |||
| 15 | .current { | ||
| 16 | font-style: normal; | ||
| 17 | font-weight: bold; | ||
| 18 | color: #ff0084; | ||
| 19 | } | ||
| 20 | |||
| 21 | a { | ||
| 22 | border: 1px solid #dddddd; | ||
| 23 | color: #0063dc; | ||
| 24 | text-decoration: none; | ||
| 25 | |||
| 26 | &:hover, &:focus { | ||
| 27 | border-color: #003366; | ||
| 28 | background: #0063dc; | ||
| 29 | color: white; | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | .page_info { | ||
| 34 | color: #aaaaaa; | ||
| 35 | padding-top: 0.8em; | ||
| 36 | } | ||
| 37 | |||
| 38 | .previous_page, .next_page { | ||
| 39 | border-width: 2px; | ||
| 40 | } | ||
| 41 | |||
| 42 | .previous_page { | ||
| 43 | margin-right: 1em; | ||
| 44 | } | ||
| 45 | |||
| 46 | .next_page { | ||
| 47 | margin-left: 1em; | ||
| 48 | } | ||
| 49 | } \ No newline at end of file | ||
| diff --git a/app/controllers/quotes_controller.rb b/app/controllers/quotes_controller.rb new file mode 100644 index 0000000..fb5e33c --- /dev/null +++ b/app/controllers/quotes_controller.rb | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | class QuotesController < ApplicationController | ||
| 2 | def index | ||
| 3 | @quote = Quote.find(310) | ||
| 4 | @qnumber = Quote.published.count | ||
| 5 | @mnumber = Quote.pending.count | ||
| 6 | end | ||
| 7 | |||
| 8 | def latest | ||
| 9 | @quotes = Quote.published.order(id: :desc).paginate(page: params[:page], per_page: 10) | ||
| 10 | |||
| 11 | respond_to do |format| | ||
| 12 | format.html { render :list } | ||
| 13 | format.json { render :json => @quotes } | ||
| 14 | format.xml { render :xml => @quotes } | ||
| 15 | format.atom { render :atom => @quotes } | ||
| 16 | end | ||
| 17 | end | ||
| 18 | |||
| 19 | def top | ||
| 20 | @quotes = Quote.published.order(Arel.sql("(upvotes - downvotes) DESC")).paginate(page: params[:page], per_page: 10) | ||
| 21 | |||
| 22 | respond_to do |format| | ||
| 23 | format.html { render :list } | ||
| 24 | format.json { render :json => @quotes } | ||
| 25 | format.xml { render :xml => @quotes } | ||
| 26 | end | ||
| 27 | end | ||
| 28 | |||
| 29 | def tags | ||
| 30 | @tags = Quote.published.tag_counts_on(:tags) | ||
| 31 | end | ||
| 32 | |||
| 33 | def tag | ||
| 34 | @quotes = Quote.published.tagged_with(params[:id]).order(id: :desc).paginate(page: params[:page], per_page: 10) | ||
| 35 | |||
| 36 | respond_to do |format| | ||
| 37 | format.html { render :list } | ||
| 38 | format.json { render :json => @quotes } | ||
| 39 | format.xml { render :xml => @quotes } | ||
| 40 | end | ||
| 41 | end | ||
| 42 | |||
| 43 | def show | ||
| 44 | @quote = Quote.published.find(params[:id]) | ||
| 45 | |||
| 46 | respond_to do |format| | ||
| 47 | format.html | ||
| 48 | format.json { render :json => @quote } | ||
| 49 | format.xml { render :xml => @quote } | ||
| 50 | end | ||
| 51 | end | ||
| 52 | |||
| 53 | def new | ||
| 54 | end | ||
| 55 | |||
| 56 | def upvote | ||
| 57 | @quote = Quote.published.find(params[:id]) | ||
| 58 | |||
| 59 | respond_to do |format| | ||
| 60 | if @quote.upvote! request.remote_ip | ||
| 61 | format.html do | ||
| 62 | flash[:notice] = "You have upvoted Quote \"#{@quote.id}\"." | ||
| 63 | redirect_to @quote | ||
| 64 | end | ||
| 65 | format.js { render "voted" } | ||
| 66 | format.xml { head :ok } | ||
| 67 | else | ||
| 68 | format.html do | ||
| 69 | flash[:notice] = "You have already voted on Quote \"#{@quote.id}\"." | ||
| 70 | redirect_to @quote | ||
| 71 | end | ||
| 72 | format.xml { render :xml => { :error => "Someone from your IP address has already voted on this quote."} } | ||
| 73 | end | ||
| 74 | end | ||
| 75 | end | ||
| 76 | |||
| 77 | def downvote | ||
| 78 | @quote = Quote.published.find(params[:id]) | ||
| 79 | |||
| 80 | respond_to do |format| | ||
| 81 | if @quote.downvote! request.remote_ip | ||
| 82 | format.html do | ||
| 83 | flash[:notice] = "You have downvoted Quote \"#{@quote.id}\"." | ||
| 84 | redirect_to @quote | ||
| 85 | end | ||
| 86 | format.js { render "voted" } | ||
| 87 | format.xml { head :ok } | ||
| 88 | else | ||
| 89 | format.html do | ||
| 90 | flash[:notice] = "You have already voted on Quote \"#{@quote.id}\"." | ||
| 91 | redirect_to @quote | ||
| 92 | end | ||
| 93 | format.xml { render :xml => { :error => "Someone from your IP address has already voted on this quote."} } | ||
| 94 | end | ||
| 95 | end | ||
| 96 | end | ||
| 97 | end | ||
| diff --git a/app/helpers/quotes_helper.rb b/app/helpers/quotes_helper.rb new file mode 100644 index 0000000..3c2acf9 --- /dev/null +++ b/app/helpers/quotes_helper.rb | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | module QuotesHelper | ||
| 2 | def quote_format(text) | ||
| 3 | text = text ? text.to_str : '' | ||
| 4 | text = text.dup if text.frozen? | ||
| 5 | text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n | ||
| 6 | text.gsub!(/\n/, '<br />') # 1 newline -> br | ||
| 7 | text = sanitize(text) | ||
| 8 | text = auto_link(text, :link => :urls) | ||
| 9 | text | ||
| 10 | end | ||
| 11 | end | ||
| diff --git a/app/models/quote.rb b/app/models/quote.rb new file mode 100644 index 0000000..d037aab --- /dev/null +++ b/app/models/quote.rb | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | class Quote < ApplicationRecord | ||
| 2 | extend Enumerize | ||
| 3 | |||
| 4 | include Votable | ||
| 5 | |||
| 6 | acts_as_taggable | ||
| 7 | |||
| 8 | validates :content, presence: true | ||
| 9 | |||
| 10 | enumerize :state, | ||
| 11 | in: [:published, :pending, :hidden], | ||
| 12 | default: :published, | ||
| 13 | predicates: true | ||
| 14 | |||
| 15 | scope :published, -> { where(state: :published) } | ||
| 16 | scope :pending, -> { where(state: :pending) } | ||
| 17 | |||
| 18 | def published_date | ||
| 19 | created_at.strftime("%B %d %Y at %I:%M:%S") + created_at.strftime(" %p").downcase + created_at.strftime(" %Z") | ||
| 20 | end | ||
| 21 | |||
| 22 | def has_extra? | ||
| 23 | has_notes? or has_tags? | ||
| 24 | end | ||
| 25 | |||
| 26 | def has_notes? | ||
| 27 | !notes.empty? | ||
| 28 | end | ||
| 29 | |||
| 30 | def has_tags? | ||
| 31 | !tags.empty? | ||
| 32 | end | ||
| 33 | end | ||
| diff --git a/app/views/layouts/quotes.html.haml b/app/views/layouts/quotes.html.haml new file mode 100644 index 0000000..6d89d91 --- /dev/null +++ b/app/views/layouts/quotes.html.haml | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | !!! 5 | ||
| 2 | %html | ||
| 3 | %head | ||
| 4 | %title The Four Island Quotes DB | ||
| 5 | %meta{ :charset => "utf-8" } | ||
| 6 | = stylesheet_link_tag "quotes" | ||
| 7 | = javascript_include_tag "application" | ||
| 8 | = csrf_meta_tag | ||
| 9 | = auto_discovery_link_tag :atom, latest_quotes_url(:atom) | ||
| 10 | %body | ||
| 11 | #wrap | ||
| 12 | %header#banner | ||
| 13 | %h1#banner-title= link_to "The Four Island Quotes DB", root_path | ||
| 14 | #banner-abbr FIQDB | ||
| 15 | .cleardiv | ||
| 16 | %nav#top-bar | ||
| 17 | %ul | ||
| 18 | %li= link_to_unless_current "Home", quotes_url | ||
| 19 | %li= link_to_unless_current "Latest", latest_quotes_url | ||
| 20 | %li= link_to_unless_current "Top", top_quotes_url | ||
| 21 | %li= link_to_unless_current "Tags", tags_quotes_url | ||
| 22 | %li= link_to_unless_current "Feed", latest_quotes_url(:atom) | ||
| 23 | .cleardiv | ||
| 24 | #page-body | ||
| 25 | - if flash[:notice] | ||
| 26 | #flash= flash[:notice] | ||
| 27 | = yield | ||
| 28 | %footer | ||
| 29 | #footer-left= raw "The Four Island Quotes DB is a #{link_to "Four Island", root_url} project and is © hatkirby 2008-#{Time.now.year}" | ||
| 30 | #footer-right #{Quote.published.count} approved quotes; #{Quote.pending.count} pending quotes | ||
| 31 | .cleardiv | ||
| diff --git a/app/views/quotes/_quote.html.haml b/app/views/quotes/_quote.html.haml new file mode 100644 index 0000000..2a9fb37 --- /dev/null +++ b/app/views/quotes/_quote.html.haml | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | %article.quote{ :id => "quote-#{quote.id}" } | ||
| 2 | %header.quote-header{ :id => "quote-header-#{quote.id}" } | ||
| 3 | = link_to_unless (quote.new_record? or current_page?(quote)), "\##{quote.id}", quote, :class => "quote-link" | ||
| 4 | %span.vote-link{ :id => "quote-upvote-link-#{quote.id}" }= link_to_unless (quote.new_record? or quote.already_upvoted?(request.remote_ip)), "Up", upvote_quote_path(quote.id), :remote => true, :rel => "nofollow", :class => "quote-upvote-link", method: :post | ||
| 5 | %span.quote-rating{ :id => "quote-rating-#{quote.id}" }= "+#{quote.upvotes}/-#{quote.downvotes}" | ||
| 6 | %span.vote-link{ :id => "quote-downvote-link-#{quote.id}" }= link_to_unless (quote.new_record? or quote.already_downvoted?(request.remote_ip)), "Down", downvote_quote_path(quote.id), :remote => true, :rel => "nofollow", :class => "quote-downvote-link", method: :post | ||
| 7 | %datetime= quote.published_date | ||
| 8 | %blockquote.quote-body= raw quote_format(h(quote.content)) | ||
| 9 | - if !quote.new_record? and quote.has_extra? | ||
| 10 | .quote-footer | ||
| 11 | - if quote.has_notes? | ||
| 12 | .quote-notes= auto_link(quote.notes, :link => :urls) | ||
| 13 | - if quote.has_tags? | ||
| 14 | %ul.quote-tags | ||
| 15 | - quote.tags.each do |tag| | ||
| 16 | %li= link_to tag.name, tag_quotes_path(tag.name) | ||
| diff --git a/app/views/quotes/index.atom.builder b/app/views/quotes/index.atom.builder new file mode 100644 index 0000000..66849cc --- /dev/null +++ b/app/views/quotes/index.atom.builder | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | atom_feed do |feed| | ||
| 2 | feed.title("The Four Island Quotes DB") | ||
| 3 | feed.updated(@quotes.first.created_at) | ||
| 4 | |||
| 5 | @quotes.each do |quote| | ||
| 6 | feed.entry(quote) do |entry| | ||
| 7 | entry.title("##{quote.id}") | ||
| 8 | entry.content(quote.content, :type => 'text') | ||
| 9 | end | ||
| 10 | end | ||
| 11 | end | ||
| diff --git a/app/views/quotes/index.html.haml b/app/views/quotes/index.html.haml new file mode 100644 index 0000000..d2f667e --- /dev/null +++ b/app/views/quotes/index.html.haml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #about-left | ||
| 2 | #quotes= render @quote | ||
| 3 | %p.normal Welcome to the Four Island Quotes Database! Here you can find many strange and hopefully humorous Four Island quotes. There are currently #{@qnumber} quotes in the database, and there are #{@mnumber} quotes awaiting moderation. | ||
| 4 | #about-right | ||
| 5 | %h3 About | ||
| 6 | %p.normal The Four Island Quotes DB is a repository for humorous and memorable quotes from #{link_to "FourNet", "http://irc.fourisland.com/"} channels, instant messaging sessions, real life situations, and more. | ||
| 7 | %p.normal The quotes database, in its first incarnation, was created on #{link_to "April 25th 2008", "http://www.fourisland.com/2008/04/quote-time/"} by hatkirby, who was inspired by #{link_to "bash.org", "http://bash.org/"} and his obsession with record-keeping to create a quotes database for Four Island. It ran on the now-defunct PHP quote management system, #{link_to "rash", "http://rqms.sourceforge.net/"}, on the subdomain "<code>quotes.fourisland.com</code>". It was rewritten by hand and integrated into Four Island (at the URL "<code>fourisland.com/quotes</code>") by hatkirby on #{link_to "June 13th 2008", "http://www.fourisland.com/2008/06/the-new-four-island/"} with the release of Four Island 2, dubbed The New Four Island. With the release of Four Island 3 on #{link_to "September 22nd, 2011", "http://www.fourisland.com/2011/09/four-island-3/"}, it was rewritten in Ruby on Rails, disassociated from Four Island and returned to its original URL. | ||
| 8 | .cleardiv | ||
| diff --git a/app/views/quotes/list.html.haml b/app/views/quotes/list.html.haml new file mode 100644 index 0000000..12c95c0 --- /dev/null +++ b/app/views/quotes/list.html.haml | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | %section#quotes | ||
| 2 | .pagination= will_paginate @quotes | ||
| 3 | = render @quotes | ||
| 4 | .pagination= will_paginate @quotes | ||
| diff --git a/app/views/quotes/new.html.erb b/app/views/quotes/new.html.erb new file mode 100644 index 0000000..a4c6a0a --- /dev/null +++ b/app/views/quotes/new.html.erb | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | <h1>Quotes#new</h1> | ||
| 2 | <p>Find me in app/views/quotes/new.html.erb</p> | ||
| diff --git a/app/views/quotes/show.html.haml b/app/views/quotes/show.html.haml new file mode 100644 index 0000000..b28c879 --- /dev/null +++ b/app/views/quotes/show.html.haml | |||
| @@ -0,0 +1 @@ | |||
| %section#quotes= render @quote | |||
| diff --git a/app/views/quotes/tags.html.haml b/app/views/quotes/tags.html.haml new file mode 100644 index 0000000..e15b386 --- /dev/null +++ b/app/views/quotes/tags.html.haml | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | %h2 Tag Cloud | ||
| 2 | %ul#tags-container | ||
| 3 | - tag_cloud(@tags, %w(css1 css1_5 css2 css2_5 css3 css3_5 css4 css4_5 css5 css5_5 css6)) do |tag, css_class| | ||
| 4 | %li= link_to tag.name, tag_quotes_path(tag.name), :class => css_class | ||
| diff --git a/app/views/quotes/voted.js.erb b/app/views/quotes/voted.js.erb new file mode 100644 index 0000000..c697c7d --- /dev/null +++ b/app/views/quotes/voted.js.erb | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | $("#quote-rating-<%= @quote.id %>").html('<%= escape_javascript("+#{@quote.upvotes}/-#{@quote.downvotes}") %>'); | ||
| 2 | |||
| 3 | <% if @quote.already_upvoted? request.remote_ip %> | ||
| 4 | $("#quote-upvote-link-<%= @quote.id %>").html("Up"); | ||
| 5 | <% elsif @quote.already_downvoted? request.remote_ip %> | ||
| 6 | $("#quote-downvote-link-<%= @quote.id %>").html("Down"); | ||
| 7 | <% else %> | ||
| 8 | $("#quote-upvote-link-<%= @quote.id %>").html('<%= escape_javascript(link_to("Up", upvote_quote_path(@quote.id), :remote => true, :rel => "nofollow", :class => "quote-upvote-link", method: :post)) %>'); | ||
| 9 | $("#quote-downvote-link-<%= @quote.id %>").html('<%= escape_javascript(link_to("Down", downvote_quote_path(@quote.id), :remote => true, :rel => "nofollow", :class => "quote-downvote-link", method: :post)) %>'); | ||
| 10 | <% end %> | ||
| 11 | |||
| 12 | $("#quote-header-<%= @quote.id %>").effect('highlight', {}, 2000); | ||
| diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index e80f11f..ac7deec 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb | |||
| @@ -12,4 +12,4 @@ Rails.application.config.assets.paths << Rails.root.join('node_modules') | |||
| 12 | # application.js, application.css, and all non-JS/CSS in the app/assets | 12 | # application.js, application.css, and all non-JS/CSS in the app/assets |
| 13 | # folder are already added. | 13 | # folder are already added. |
| 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) | 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) |
| 15 | Rails.application.config.assets.precompile += %w( main userdata admin ) | 15 | Rails.application.config.assets.precompile += %w( main userdata admin quotes ) |
| diff --git a/config/routes.rb b/config/routes.rb index 6363590..33cc5f3 100644 --- a/config/routes.rb +++ b/config/routes.rb | |||
| @@ -55,6 +55,20 @@ Rails.application.routes.draw do | |||
| 55 | end | 55 | end |
| 56 | end | 56 | end |
| 57 | 57 | ||
| 58 | resources :quotes do | ||
| 59 | collection do | ||
| 60 | get 'latest' | ||
| 61 | get 'top' | ||
| 62 | get 'tags' | ||
| 63 | get 'tags/:id', :action => "tag", :as => "tag" | ||
| 64 | end | ||
| 65 | |||
| 66 | member do | ||
| 67 | post 'upvote' | ||
| 68 | post 'downvote' | ||
| 69 | end | ||
| 70 | end | ||
| 71 | |||
| 58 | mount Pokeviewer::Engine => '/poke3' | 72 | mount Pokeviewer::Engine => '/poke3' |
| 59 | mount Lingo::Engine => '/lingo' | 73 | mount Lingo::Engine => '/lingo' |
| 60 | end | 74 | end |
| diff --git a/db/migrate/20231021020306_create_quotes.rb b/db/migrate/20231021020306_create_quotes.rb new file mode 100644 index 0000000..60424e5 --- /dev/null +++ b/db/migrate/20231021020306_create_quotes.rb | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | class CreateQuotes < ActiveRecord::Migration[7.0] | ||
| 2 | def change | ||
| 3 | create_table :quotes do |t| | ||
| 4 | t.text :content, null: false | ||
| 5 | t.string :state, null: false | ||
| 6 | t.string :submitter | ||
| 7 | t.text :notes, null: false | ||
| 8 | t.integer :upvotes, null: false, default: 0 | ||
| 9 | t.integer :downvotes, null: false, default: 0 | ||
| 10 | |||
| 11 | t.timestamps | ||
| 12 | end | ||
| 13 | end | ||
| 14 | end | ||
| diff --git a/db/schema.rb b/db/schema.rb index f8a8c49..541f1c1 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 | ||
| 13 | ActiveRecord::Schema[7.0].define(version: 2023_10_20_195330) do | 13 | ActiveRecord::Schema[7.0].define(version: 2023_10_21_020306) 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 |
| @@ -324,6 +324,17 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_20_195330) do | |||
| 324 | t.index ["world_ribbon_id"], name: "index_pokeviewer_trainers_on_world_ribbon_id" | 324 | t.index ["world_ribbon_id"], name: "index_pokeviewer_trainers_on_world_ribbon_id" |
| 325 | end | 325 | end |
| 326 | 326 | ||
| 327 | create_table "quotes", force: :cascade do |t| | ||
| 328 | t.text "content", null: false | ||
| 329 | t.string "state", null: false | ||
| 330 | t.string "submitter" | ||
| 331 | t.text "notes", null: false | ||
| 332 | t.integer "upvotes", default: 0, null: false | ||
| 333 | t.integer "downvotes", default: 0, null: false | ||
| 334 | t.datetime "created_at", null: false | ||
| 335 | t.datetime "updated_at", null: false | ||
| 336 | end | ||
| 337 | |||
| 327 | create_table "records", force: :cascade do |t| | 338 | create_table "records", force: :cascade do |t| |
| 328 | t.text "description" | 339 | t.text "description" |
| 329 | t.string "recordable_type", limit: 191 | 340 | t.string "recordable_type", limit: 191 |
| diff --git a/test/controllers/quotes_controller_test.rb b/test/controllers/quotes_controller_test.rb new file mode 100644 index 0000000..a085d40 --- /dev/null +++ b/test/controllers/quotes_controller_test.rb | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | require "test_helper" | ||
| 2 | |||
| 3 | class QuotesControllerTest < ActionDispatch::IntegrationTest | ||
| 4 | test "should get index" do | ||
| 5 | get quotes_index_url | ||
| 6 | assert_response :success | ||
| 7 | end | ||
| 8 | |||
| 9 | test "should get show" do | ||
| 10 | get quotes_show_url | ||
| 11 | assert_response :success | ||
| 12 | end | ||
| 13 | |||
| 14 | test "should get new" do | ||
| 15 | get quotes_new_url | ||
| 16 | assert_response :success | ||
| 17 | end | ||
| 18 | end | ||
| diff --git a/test/fixtures/quotes.yml b/test/fixtures/quotes.yml new file mode 100644 index 0000000..761703f --- /dev/null +++ b/test/fixtures/quotes.yml | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html | ||
| 2 | |||
| 3 | one: | ||
| 4 | content: MyText | ||
| 5 | state: MyString | ||
| 6 | submitter: MyString | ||
| 7 | notes: MyText | ||
| 8 | upvotes: 1 | ||
| 9 | downvotes: 1 | ||
| 10 | |||
| 11 | two: | ||
| 12 | content: MyText | ||
| 13 | state: MyString | ||
| 14 | submitter: MyString | ||
| 15 | notes: MyText | ||
| 16 | upvotes: 1 | ||
| 17 | downvotes: 1 | ||
| diff --git a/test/models/quote_test.rb b/test/models/quote_test.rb new file mode 100644 index 0000000..d58ff5c --- /dev/null +++ b/test/models/quote_test.rb | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | require "test_helper" | ||
| 2 | |||
| 3 | class QuoteTest < ActiveSupport::TestCase | ||
| 4 | # test "the truth" do | ||
| 5 | # assert true | ||
| 6 | # end | ||
| 7 | end | ||
