Compare commits

..

11 Commits

Author SHA1 Message Date
Daniel O'Connor
ad17a6a7c6 Merge branch 'mainline' into dev 2024-02-04 16:18:40 +10:30
Daniel O'Connor
1d1eddb228 Merge pull request #3625 from Growstuff/issue/3404
Flex into column mode on medium screens
2024-02-04 16:16:21 +10:30
Daniel O'Connor
5610c287cf Merge pull request #3623 from Growstuff/issue/3614
Fix iCalendar output when predicted date is passed, as a string.
2024-02-04 16:15:50 +10:30
Daniel O'Connor
c0dbe9616a Flex into column mode on medium screens 2024-02-04 05:37:12 +00:00
Daniel O'Connor
27b25d2b4b Merge pull request #3622 from Growstuff/more-extensions
Add yaml, haml, erb to devcontainer [ci-skip]
2024-02-04 15:54:32 +10:30
Daniel O'Connor
e2bdbeff23 Parse date 2024-02-04 05:18:57 +00:00
Daniel O'Connor
50ba0f08b1 Fix Plantings > CSV output (#3616)
* Fix crash

* Add test coverage and index more

* Fix test
2024-02-04 15:09:30 +10:30
Daniel O'Connor
49284eb169 Fix haml preview (#3610)
* HAML

* rewrite

* Fix specs - but likely still wrong

* Return temple

* Trailing line

* Fix specs

* This was rearranged, apparently.

* Fix tests

* Retain escaping

* Fix specs

* Rubocop

* Attempt to fix rendering

* Fix output

* Move away from filter

* Move away from filter

* Fix spec

* Fix specs

* Fix structure to avoid nested paragraph tags
2024-02-04 15:08:18 +10:30
Daniel O'Connor
3738d7cc21 Seeds > Photos - Fix tests not running and fix tests (#3621)
* Rename, so it runs in CI

* Fix specs slightly

* Fix tests
2024-02-04 15:05:43 +10:30
Daniel O'Connor
1f46bd8047 Add yaml, haml, erb to devcontainer 2024-02-04 04:33:35 +00:00
Daniel O'Connor
0623dbbeb6 Merge pull request #3613 from Growstuff/dev
Revert 140fe7a (#3612)
2024-02-04 13:30:16 +10:30
32 changed files with 163 additions and 95 deletions

View File

@@ -20,10 +20,18 @@
// these don't actually work as postCreateCommands, you need to run them manually
// for some unknown reason, Github codespaces use rbenv and rvm simultaneously
// and you need both to be correct for it to work
"postCreateCommand": "nice -n 19 bundle install && nice -n 19 bundle exec rake db:create && nice -n 19 bundle exec rake db:migrate && nice -n19 bundle exec rake db:seed"
"postCreateCommand": "nice -n 19 bundle install && nice -n 19 bundle exec rake db:create && nice -n 19 bundle exec rake db:migrate && nice -n19 bundle exec rake db:seed",
// Configure tool-specific properties.
// "customizations": {},
"customizations": {
"vscode": {
"extensions": [
"karunamurti.haml",
"redhat.vscode-yaml",
"CraigMaslowski.erb"
]
}
}
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"

View File

@@ -54,6 +54,12 @@
margin-bottom: 0;
}
.card-footer {
.d-flex {
flex-direction: column;
}
}
.sidebar {
border-left: 0;
margin-left: 0;

View File

@@ -35,7 +35,7 @@ class SeedsController < DataController
end
def show
@photos = @seed.photos.includes(:owner).order(created_at: :desc).paginate(page: params[:page])
@photos = @seed.photos.includes(:owner).order(created_at: :desc, id: :desc).paginate(page: params[:page], per_page: 30)
respond_with(@seed)
end

View File

@@ -34,6 +34,13 @@ module ApplicationHelper
tag.div(asterisk + ' '.html_safe + text, class: ['margin-bottom'])
end
# A helper to replace the complex template compilation mess
# of HAML, Tilt, and dynamic compilation with interpolated ruby.
def markdownify(text)
translator = Haml::Filters::GrowstuffMarkdown.new
translator.expand_members!(translator.expand_crops!(text.to_s))
end
#
# Returns an image uri for a given member.
#

View File

@@ -1,12 +1,7 @@
# frozen_string_literal: true
module PostsHelper
def display_post_truncated(post, length: 300)
truncate(strip_tags(post.body), length:,
separator: ' ', omission: '... ') { link_to "Read more", post_path(post) }
end
def post_stripped_tags(post, length: 300)
truncate(strip_tags(post.body), length:)
truncate(strip_tags(markdownify(post.body)), length:)
end
end

View File

@@ -30,6 +30,8 @@ module SearchPlantings
quantity:,
sunniness:,
garden_id:,
garden_name: garden&.name,
description:,
first_harvest_predicted_at:,
finish_predicted_at:,
@@ -53,7 +55,8 @@ module SearchPlantings
harvests_count:,
# timestamps
created_at: created_at.to_i
created_at: created_at.to_i,
updated_at: updated_at.to_i
}
end

View File

@@ -31,6 +31,6 @@
= comment.updated_at
.comment-body
:growstuff_markdown
#{ strip_tags comment.body }
:markdown
#{ strip_tags markdownify(comment.body) }

View File

@@ -14,7 +14,7 @@
</p>
:escaped_markdown
#{ strip_tags comment.body }
#{ strip_tags markdownify(comment.body) }
%pubdate= comment.created_at.to_fs(:rfc822)
%link= post_url(comment.post)

View File

@@ -26,8 +26,8 @@
= render 'members/tiny', member: message.sender
.col-12
%p.text-justify
:growstuff_markdown
#{ strip_tags(message.body) }
:markdown
#{ strip_tags markdownify(message.body) }
%li.list-group-item
= icon 'fas', 'reply'
= render 'messages/form', conversation: @conversation

View File

@@ -58,10 +58,10 @@ csv.headers *all_headers
end
csv.cell :plantings_count, c.plantings_count || 0
csv.cell :seeds_count, c.seeds.size
csv.cell :seeds_count, c.seeds.size[]
csv.cell :harvests_count, c.harvests.size
# Sunniness
# Sunniness
sunniness = c.sunniness
sunniness_rec = sunniness.max_by{|k,v| v}

View File

@@ -16,8 +16,8 @@
= link_to @forum.owner, @forum.owner
%div
:growstuff_markdown
#{ strip_tags(@forum.description) }
:markdown
#{ strip_tags markdownify(@forum.description) }
- if can? :edit, @forum
= link_to "Edit", edit_forum_path(@forum), class: 'btn btn-default btn-xs'

View File

@@ -29,8 +29,8 @@
to plant something in this garden.
%div
%p
:growstuff_markdown
#{strip_tags @garden.description}
:markdown
#{strip_tags markdownify(@garden.description)}
- unless @garden.description
.row-fluid
%p No description available yet.

View File

@@ -58,8 +58,8 @@
.card-header
%h2 Notes
.card-body
:growstuff_markdown
#{strip_tags(@harvest.description)}
:markdown
#{strip_tags markdownify(@harvest.description)}
.col-md-4.col-xs-12
= render 'harvests/owner', harvest: @harvest

View File

@@ -5,5 +5,5 @@
- else
#{member.login_name} hasn't written a bio yet.
- else
:growstuff_markdown
#{ strip_tags member.bio }
:markdown
#{ strip_tags markdownify(member.bio) }

View File

@@ -11,4 +11,4 @@
%guid= post_url(post)
%description
:escaped_markdown
#{ strip_tags post.body }
#{ strip_tags markdownify(post.body) }

View File

@@ -12,8 +12,8 @@
\.
%blockquote
:growstuff_markdown
#{strip_tags @notification.body}
:markdown
#{strip_tags markdownify(@notification.body)}
%p
= link_to "Reply to this message", @reply_link

View File

@@ -1,7 +1,8 @@
.photo.hero.jumbotron
.row
.col-6
= image_tag(photo.fullsize_url, alt: photo.title, class: 'img img-responsive hero-photo')
= image_tag(photo.thumbnail_url, alt: photo.title, class: 'img img-responsive hero-photo')
.col-6
%h3= link_to photo.title, photo
Taken on #{I18n.l photo.date_taken}
- if photo.date_taken
Taken on #{I18n.l photo.date_taken}

View File

@@ -18,31 +18,32 @@ csv.headers :id,
:license
@plantings.each do |p|
puts p.inspect
csv.row p do |csv, planting|
csv.cell :id
csv.cell :growstuff_url, planting_url(p)
csv.cell :growstuff_url, planting_url(p.slug)
csv.cell :owner_id, p.owner.id
csv.cell :owner_name, p.owner.to_s
csv.cell :owner_id, p.owner_id
csv.cell :owner_name, p.owner_login_name
csv.cell :garden_id, p.garden.id
csv.cell :garden_name, p.garden.to_s
csv.cell :garden_id, p.garden_id
csv.cell :garden_name, p.garden_name
csv.cell :crop_id, p.crop.id
csv.cell :crop_name, p.crop.to_s
csv.cell :crop_id, p.crop_id
csv.cell :crop_name, p.crop_name
csv.cells :quantity, :planted_from, :sunniness
csv.cell :date_planted, p.planted_at ? p.planted_at.to_fs(:db) : ''
csv.cell :date_planted, p.planted_at ? p.planted_at : ''
csv.cell :finished
csv.cell :date_finished, p.finished_at ? p.finished_at.to_fs(:db) : ''
csv.cell :date_finished, p.finished_at ? p.finished_at : ''
csv.cell :description
csv.cell :description, p.description
csv.cell :date_added, p.created_at.to_fs(:db)
csv.cell :last_modified, p.updated_at.to_fs(:db)
csv.cell :date_added, Time.at(p.created_at).to_fs(:db)
csv.cell :last_modified, Time.at(p.updated_at).to_fs(:db)
csv.cell :license, "CC-BY-SA Growstuff http://growstuff.org/"

View File

@@ -29,8 +29,10 @@ cal.description = "Plantings by #{@owner.login_name}"
cal.add_event(event)
if finish_date && finish_date > Date.today
predicted_date = Date.parse(planting['first_harvest_predicted_at']) if planting['first_harvest_predicted_at']
todo = Icalendar::Todo.new
todo.dtstart = planting['first_harvest_predicted_at'] || finish_date || Date.today
todo.dtstart = predicted_date || finish_date || Date.today
todo.due = finish_date
todo.summary = "Harvest #{planting['crop_name']}"

View File

@@ -15,6 +15,6 @@
<p>Sunniness: #{planting['sunniness'] ? planting['sunniness'] : 'unknown' }</p>
<p>Planted from: #{planting['planted_from'] ? planting['planted_from'] : 'unknown' }</p>
:escaped_markdown
#{ strip_tags planting['description'] }
#{ strip_tags markdownify(planting['description']) }
%link= planting_url(slug: planting['slug'])
%guid= planting_url(slug: planting['slug'])

View File

@@ -52,8 +52,8 @@
.card-header
%h2 Notes
.card-body
:growstuff_markdown
#{strip_tags(@planting.description)}
:markdown
#{strip_tags markdownify(@planting.description)}
%section= render 'plantings/photos', photos: @photos, planting: @planting
%section.harvests

View File

@@ -10,7 +10,9 @@
by
= succeed "," do
%a.font-weight-bold= post.author
%p.post-body= post_stripped_tags(post, length: 300)
.post-body
:markdown
#{post_stripped_tags(post, length: 300)}
%h6.font-weight-bold.mb-3
- post.crops.each do |crop|
= link_to crop do

View File

@@ -15,5 +15,5 @@
= link_to "Permalink", post
:growstuff_markdown
#{ strip_tags @post.body }
:markdown
#{ strip_tags markdownify(@post.body) }

View File

@@ -9,7 +9,7 @@
%title #{post.subject} by #{post.author.login_name}
%description
:escaped_markdown
#{ strip_tags post.body }
#{ strip_tags markdownify(post.body) }
%pubdate= post.created_at.to_fs(:rfc822)
%link= post_url(post)
%guid= post_url(post)

View File

@@ -15,7 +15,7 @@
</p>
:escaped_markdown
#{ strip_tags comment.body }
#{ strip_tags markdownify(comment.body) }
%pubdate= comment.created_at.to_fs(:rfc822)
%link= post_url(@post)

View File

@@ -45,8 +45,8 @@
.card-header
%h2 Notes
.card-body
:growstuff_markdown
#{strip_tags(@seed.description)}
:markdown
#{strip_tags markdownify(@seed.description)}
- if current_member
- if @seed.tradable && current_member != @seed.owner

View File

@@ -4,7 +4,7 @@ require 'bluecloth'
require 'haml/filters/growstuff_markdown'
class Haml::Filters
class EscapedMarkdown < Haml::Filters::GrowstuffMarkdown
class EscapedMarkdown < Haml::Filters::Markdown
def compile(node)
[:escape, true, super(node)]
end

View File

@@ -1,29 +1,17 @@
# frozen_string_literal: true
require 'bluecloth'
# TODO: Move this file/helper elsewhere, as it is used as a pre filter rather than plugging into the haml architecture
class Haml::Filters
class GrowstuffMarkdown < Haml::Filters::Markdown
def compile(node)
@expanded = node.value[:text]
expand_crops!
expand_members!
node.value[:text] = @expanded
compile_with_tilt(node, 'markdown')
end
private
class GrowstuffMarkdown
CROP_REGEX = /(?<!\\)\[([^\[\]]+?)\]\(crop\)/
MEMBER_REGEX = /(?<!\\)\[([^\[\]]+?)\]\(member\)/
MEMBER_AT_REGEX = /(?<!\\)(@\w+)/
MEMBER_ESCAPE_AT_REGEX = /(?<!\\)\\(?=@\w+)/
HOST = Rails.application.config.host
def expand_crops!
def expand_crops!(text)
# turn [tomato](crop) into [tomato](http://growstuff.org/crops/tomato)
@expanded = @expanded.gsub(CROP_REGEX) do
text.gsub(CROP_REGEX) do
crop_str = Regexp.last_match(1)
# find crop case-insensitively
crop = Crop.where('lower(name) = ?', crop_str.downcase).first
@@ -31,18 +19,18 @@ class Haml::Filters
end
end
def expand_members!
def expand_members!(text)
# turn [jane](member) into [jane](http://growstuff.org/members/jane)
# turn @jane into [@jane](http://growstuff.org/members/jane)
[MEMBER_REGEX, MEMBER_AT_REGEX].each do |re|
@expanded = @expanded.gsub(re) do
text = text.gsub(re) do
member_str = Regexp.last_match(1)
member = find_member(member_str)
member_link(member, member_str)
end
end
@expanded = @expanded.gsub(MEMBER_ESCAPE_AT_REGEX, '')
text.gsub(MEMBER_ESCAPE_AT_REGEX, '')
end
def member_link(member, link_text)
@@ -69,8 +57,4 @@ class Haml::Filters
Member.case_insensitive_login_name(login_name).first
end
end
# Register it as the handler for the :growstuff_markdown HAML command.
# The automatic system gives us :growstuffmarkdown, which is ugly.
Haml::Filters.registered[:growstuff_markdown] = GrowstuffMarkdown
end

View File

@@ -13,39 +13,52 @@ describe "Seeds", :js do
let(:member) { FactoryBot.create(:member) }
let!(:seed) { FactoryBot.create(:seed, owner: member) }
it { is_expected.to have_content 'Add photo' }
it {
click_on "Actions"
expect(subject).to have_content 'Add photo'
}
# context 'no photos' do
# it { is_expected.to have_content 'no photos' }
# end
context 'has one photo' do
before { seed.photos = [photo] }
before do
seed.photos = [photo]
visit seed_path(seed)
end
let!(:photo) { FactoryBot.create(:photo, title: 'hello photo') }
let!(:photo) { FactoryBot.create(:photo, title: 'hello photo', owner: seed.owner) }
it { is_expected.to have_xpath("//img[contains(@src,'#{photo.thumbnail_url}')]") }
it { is_expected.to have_xpath("//a[contains(@href,'#{photo_path(photo)}')]") }
end
context 'has 50 photos' do
before { seed.photos = photos }
before do
seed.photos = photos
let!(:photos) { FactoryBot.create_list(:photo, 50) }
visit seed_path(seed)
end
let!(:photos) { FactoryBot.create_list(:photo, 10 * 5, owner: seed.owner) }
let(:newest_photo) { seed.photos.order(created_at: :desc, id: :desc).first }
let(:oldest_photo) { seed.photos.order(created_at: :desc, id: :desc).last }
it "shows newest photo" do
expect(subject).to have_xpath("//img[contains(@src,'#{photos.last.thumbnail_url}')]")
expect(subject).to have_xpath("//img[contains(@src,'#{newest_photo.thumbnail_url}')]")
end
it "links to newest photo" do
expect(subject).to have_xpath("//a[contains(@href,'#{photo_path(photos.last)}')]")
expect(subject).to have_xpath("//a[contains(@href,'#{photo_path(newest_photo)}')]")
end
it "does not show oldest photo" do
expect(subject).not_to have_xpath("//img[contains(@src,'#{photos.first.thumbnail_url}')]")
expect(subject).to have_no_xpath("//img[contains(@src,'#{oldest_photo.thumbnail_url}')]")
end
it "does not link to oldest photo" do
expect(subject).not_to have_xpath("//a[contains(@href,'#{photo_path(photos.first)}')]")
expect(subject).to have_no_xpath("//a[contains(@href,'#{photo_path(oldest_photo)}')]")
end
end
end

View File

@@ -25,10 +25,7 @@ def output_member_link(member, name = nil)
end
describe 'Haml::Filters::Growstuff_Markdown' do
it 'is registered as the handler for :growstuff_markdown' do
Haml::Filters.registered[:growstuff_markdown].should ==
Haml::Filters::GrowstuffMarkdown
end
include ApplicationHelper
it 'converts quick crop links' do
@crop = FactoryBot.create(:crop)
@@ -120,8 +117,8 @@ describe 'Haml::Filters::Growstuff_Markdown' do
def haml_template(input)
<<~HTML
:growstuff_markdown
#{input}
:markdown
#{markdownify input}
HTML
end

View File

@@ -14,13 +14,15 @@ describe "Plantings" do
context "with a member" do
before do
@member = create(:interesting_member)
@predictable_planting = create(:predictable_planting, owner: @member)
@predictable_planting = create(:predictable_planting, owner: @member, planted_at: 1.days.ago, days_to_first_harvest: 10, days_to_last_harvest: 20)
@predictable_planting.crop.update(median_days_to_first_harvest: 10)
@seedling_planting = create(:seedling_planting, owner: @member)
@seed_planting = create(:seed_planting, owner: @member)
@finished_planting = create(:finished_planting, owner: @member)
@annual_planting = create(:annual_planting, owner: @member)
@perennial_planting = create(:perennial_planting, owner: @member)
Planting.reindex
end
@@ -50,5 +52,52 @@ describe "Plantings" do
response.status.should be(200)
end
end
describe "GET /members/x/plantings.csv" do
let(:expected_headers) do
[
"Id",
"Growstuff url",
"Owner",
"Owner name",
"Garden",
"Garden name",
"Crop",
"Crop name",
"Quantity",
"Planted from",
"Sunniness",
"Date planted",
"Finished",
"Date finished",
"Description",
"Date added",
"Last modified",
"License"
]
end
it "works!" do
get member_plantings_path(@member, format: "csv")
response.status.should be(200)
data = CSV.parse(response.body, headers: true)
expect(data.headers).to eq expected_headers
expect(data[1]["Crop name"]).to eq @predictable_planting.crop.name
expect(data[1]["Owner name"]).to eq @member.to_s
expect(data[1]["Garden name"]).to eq @predictable_planting.garden.to_s
expect(data[1]["Description"]).to eq @predictable_planting.description
expect(data[1]["Date planted"]).to eq @predictable_planting.planted_at.to_fs(:db)
expect(data[1]["Quantity"].to_i).to eq @predictable_planting.quantity
expect(data[1]["Sunniness"]).to eq @predictable_planting.sunniness
expect(data[1]["Planted from"]).to eq @predictable_planting.planted_from
expect(data[1]["Date added"]).to eq @predictable_planting.created_at.to_fs(:db)
expect(data[1]["License"]).to eq "CC-BY-SA Growstuff http://growstuff.org/"
expect(data.count).to eq 6
end
end
end
end

View File

@@ -24,7 +24,7 @@ describe "posts/index" do
it "renders a list of posts" do
assert_select "div.post", count: 2
assert_select "h4", text: "A Post", count: 2
assert_select "p.post-body", text: "This is some text.", count: 2
assert_select ".post-body p", text: "This is some text.", count: 2
end
it "contains two gravatar icons" do