FIX: Pull hotlinked images for lightbox links as well.

FIX: Pull hotlinked images for lightbox links as well.

diff --git a/app/jobs/regular/pull_hotlinked_images.rb b/app/jobs/regular/pull_hotlinked_images.rb
index e76c4b6..d3e3d07 100644
--- a/app/jobs/regular/pull_hotlinked_images.rb
+++ b/app/jobs/regular/pull_hotlinked_images.rb
@@ -56,8 +56,8 @@ module Jobs
       has_new_broken_image = false
       has_downloaded_image = false
 
-      extract_images_from(post.cooked).each do |image|
-        src = original_src = image['src']
+      extract_images_from(post.cooked).each do |node|
+        src = original_src = node['src'] || node['href']
         src = "#{SiteSetting.force_https ? "https" : "http"}:#{src}" if src.start_with?("//")
 
         if should_download_image?(src)
@@ -91,9 +91,20 @@ module Jobs
             if downloaded_urls[src].present?
               url = downloaded_urls[src]
               escaped_src = Regexp.escape(original_src)
+
               # there are 6 ways to insert an image in a post
               # HTML tag - <img src="http://...">
               raw.gsub!(/src=["']#{escaped_src}["']/i, "src='#{url}'")
+
+              if (original_path = Upload.extract_url(original_src)&.to_s) &&
+                 (path = Upload.extract_url(url)&.to_s)
+
+                raw.gsub!(
+                  /src=["']\S*#{Regexp.escape(original_path)}["']/i,
+                  "src='#{url}'"
+                )
+              end
+
               # BBCode tag - [img]http://...[/img]
               raw.gsub!(/\[img\]#{escaped_src}\[\/img\]/i, "[img]#{url}[/img]")
               # Markdown linked image - [![alt](http://...)](http://...)
@@ -136,7 +147,7 @@ module Jobs
 
     def extract_images_from(html)
       doc = Nokogiri::HTML::fragment(html)
-      doc.css("img[src]") - doc.css("img.avatar") - doc.css(".lightbox img[src]")
+      doc.css("img[src], a.lightbox[href]") - doc.css("img.avatar") - doc.css(".lightbox img[src]")
     end
 
     def should_download_image?(src)
diff --git a/spec/fabricators/optimized_image_fabricator.rb b/spec/fabricators/optimized_image_fabricator.rb
index f5311b9..01523d6 100644
--- a/spec/fabricators/optimized_image_fabricator.rb
+++ b/spec/fabricators/optimized_image_fabricator.rb
@@ -6,6 +6,9 @@ Fabricator(:optimized_image) do
   extension ".png"
   width 100
   height 200
-  url "138569_100x200.png"
   version OptimizedImage::VERSION
+
+  after_build do |optimized_image, _|
+    optimized_image.url = Discourse.store.get_path_for_optimized_image(optimized_image)
+  end
 end
diff --git a/spec/jobs/pull_hotlinked_images_spec.rb b/spec/jobs/pull_hotlinked_images_spec.rb
index ba92da9..f87bc9b 100644
--- a/spec/jobs/pull_hotlinked_images_spec.rb
+++ b/spec/jobs/pull_hotlinked_images_spec.rb
@@ -68,6 +68,25 @@ describe Jobs::PullHotlinkedImages do
       expect(post.raw).to match(/^<img src='\/uploads/)
     end
 
+    it 'replaces optimized images' do
+      optimized_image = Fabricate(:optimized_image)
+      url = "#{Discourse.base_url}#{optimized_image.url}"
+
+      stub_request(:get, url)
+        .to_return(status: 200, body: file_from_fixtures("smallest.png"))
+
+      post = Fabricate(:post, raw: "<img src='#{url}'>")
+
+      expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }
+        .to change { Upload.count }.by(1)
+
+      upload = Upload.last
+      post.reload
+
+      expect(post.raw).to eq("<img src='#{upload.url}'>")
+      expect(post.uploads).to contain_exactly(upload)
+    end
+
     describe 'onebox' do
       let(:media) { "File:Brisbane_May_2013201.jpg" }
       let(:url) { "https://commons.wikimedia.org/wiki/#{media}" }
@@ -172,18 +191,52 @@ describe Jobs::PullHotlinkedImages do
 
   describe "with a lightboxed image" do
     fab!(:upload) { Fabricate(:upload) }
+    fab!(:user) { Fabricate(:user) }
 
     before do
-      FastImage.expects(:size).returns([1750, 2000])
+      FastImage.expects(:size).returns([1750, 2000]).at_least_once
       OptimizedImage.stubs(:resize).returns(true)
+      Jobs.run_immediately!
+    end
+
+    it 'replaces missing local uploads in lightbox link' do
+      post = PostCreator.create!(
+        user,
+        raw: "<img src='#{Discourse.base_url}#{upload.url}'>",
+        title: "Some title that is long enough"
+      )
+
+      expect(post.reload.cooked).to have_tag(:a, with: { class: "lightbox" })
+
+      stub_request(:get, "#{Discourse.base_url}#{upload.url}")
+        .to_return(status: 200, body: file_from_fixtures("smallest.png"))
+
+      upload.delete
+
+      expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }
+        .to change { Upload.count }.by(1)
+
+      post.reload
+
+      expect(post.raw).to eq("<img src='#{Upload.last.url}'>")
+      expect(post.uploads.count).to eq(1)
     end
 
     it "doesn't remove optimized images from lightboxes" do
-      post = Fabricate(:post, raw: "![alt](#{upload.short_url})")
-      Jobs::ProcessPost.new.execute(post_id: post.id)
+      post = PostCreator.create!(
+        user,
+        raw: "![alt](#{upload.short_url})",
+        title: "Some title that is long enough"
+      )
+
+      expect(post.reload.cooked).to have_tag(:a, with: { class: "lightbox" })
+
+      expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }
+        .not_to change { Upload.count }
+
+      post.reload
 
-      expect { Jobs::PullHotlinkedImages.new.execute(post_id: post.id) }.not_to change { Upload.count }
-      expect(post.reload.cooked).to include "/uploads/default/optimized/" # Ensure the lightbox was actually rendered
+      expect(post.raw).to eq("![alt](#{upload.short_url})")
     end
   end

GitHub sha: df1e6eed

Fix the build.

DEV: Remove unused variable.