--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24 48C37.2548 48 48 37.2548 48 24C48 10.7452 37.2548 0 24 0C10.7452 0 0 10.7452 0 24C0 37.2548 10.7452 48 24 48Z" fill="#0971BD"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.8 17.1754C12.8 17.1754 22.4595 22.653 24.3344 22.653C26.2995 22.653 35.8544 17.28 35.8544 17.28L35.8698 16.32C35.8698 15.2602 35.0096 14.4 33.9469 14.4H14.7219C13.6602 14.4 12.8 15.2602 12.8 16.32V17.1754ZM35.8544 20.16C35.8544 20.16 26.3898 25.293 24.3344 25.293C22.4595 25.293 12.8144 20.16 12.8154 20.16L12.8 31.68C12.8 32.7399 13.6611 33.6 14.7219 33.6H33.9469C35.0096 33.6 35.8698 32.7399 35.8698 31.68L35.8544 20.16Z" fill="white"/>
+</svg>
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z" fill="#3B5998"/>
+<path d="M26.5015 38.1115V25.0542H30.1059L30.5836 20.5546H26.5015L26.5077 18.3025C26.5077 17.1289 26.6192 16.5001 28.3048 16.5001H30.5581V12H26.9532C22.6231 12 21.0991 14.1828 21.0991 17.8536V20.5551H18.4V25.0547H21.0991V38.1115H26.5015Z" fill="white"/>
+</svg>
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z" fill="#0077B5"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3188 14.8227C17.3188 16.3918 16.1377 17.6473 14.2412 17.6473H14.2064C12.3805 17.6473 11.2 16.3918 11.2 14.8227C11.2 13.2204 12.4164 12 14.277 12C16.1377 12 17.2835 13.2204 17.3188 14.8227ZM16.9605 19.8778V36.2196H11.5216V19.8778H16.9605ZM36.5752 36.2196L36.5754 26.8497C36.5754 21.8303 33.8922 19.4941 30.3131 19.4941C27.4254 19.4941 26.1325 21.0802 25.4107 22.1929V19.8783H19.9711C20.0428 21.4117 19.9711 36.22 19.9711 36.22H25.4107V27.0934C25.4107 26.605 25.446 26.1178 25.5898 25.7681C25.9829 24.7924 26.8779 23.7822 28.3805 23.7822C30.3494 23.7822 31.1365 25.2807 31.1365 27.4767V36.2196H36.5752Z" fill="white"/>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg width="800px" height="800px" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
+ <circle cx="512" cy="512" r="512" style="fill:#2b90d9"/>
+ <path d="M750.7 448.1c0-111-72.8-143.6-72.8-143.6-36.7-16.9-99.7-23.9-165.1-24.5h-1.6c-65.4.5-128.4 7.6-165.1 24.5 0 0-72.8 32.5-72.8 143.6 0 25.4-.5 55.8.3 88.1 2.6 108.6 19.9 215.6 120.3 242.2 46.3 12.2 86 14.8 118.1 13.1 58.1-3.2 90.7-20.7 90.7-20.7l-1.9-42.1s-41.5 13.1-88.1 11.5c-46.2-1.6-94.9-5-102.4-61.7-.7-5.3-1-10.6-1-15.9 0 0 45.3 11.1 102.7 13.7 35.1 1.6 68-2.1 101.5-6 64.1-7.7 120-47.2 127-83.3 11.1-56.9 10.2-138.9 10.2-138.9zm-85.8 143.1h-53.3V460.7c0-27.5-11.6-41.5-34.7-41.5-25.6 0-38.4 16.6-38.4 49.3V540h-53v-71.5c0-32.8-12.8-49.3-38.4-49.3-23.1 0-34.7 14-34.7 41.5v130.5h-53.3V456.8c0-27.5 7-49.3 21.1-65.5 14.5-16.2 33.5-24.4 57-24.4 27.3 0 47.9 10.5 61.6 31.4l13.2 22.2 13.3-22.2c13.7-21 34.3-31.4 61.6-31.4 23.5 0 42.5 8.3 57 24.4 14 16.1 21 38 21 65.5v134.4z" style="fill:#fff"/>
+</svg>
\ No newline at end of file
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24 48C37.2548 48 48 37.2548 48 24C48 10.7452 37.2548 0 24 0C10.7452 0 0 10.7452 0 24C0 37.2548 10.7452 48 24 48Z" fill="url(#paint0_linear_34411_836)"/>
+<path d="M19.6 35C18.8225 35 18.9546 34.7064 18.6864 33.9661L16.4 26.4412L34 16" fill="#C8DAEA"/>
+<path d="M19.6 34.9999C20.2 34.9999 20.4651 34.7255 20.8 34.3999L24 31.2883L20.0084 28.8813" fill="#A9C9DD"/>
+<path d="M20.008 28.882L29.68 36.0278C30.7837 36.6368 31.5803 36.3215 31.8552 35.0031L35.7922 16.4505C36.1953 14.8344 35.1762 14.1015 34.1203 14.5808L11.0023 23.495C9.4243 24.128 9.4335 25.0084 10.7147 25.4006L16.6473 27.2523L30.3819 18.5873C31.0303 18.1941 31.6253 18.4055 31.1369 18.839" fill="url(#paint1_linear_34411_836)"/>
+<defs>
+<linearGradient id="paint0_linear_34411_836" x1="18.0024" y1="2.0016" x2="6.0024" y2="30" gradientUnits="userSpaceOnUse">
+<stop stop-color="#37AEE2"/>
+<stop offset="1" stop-color="#1E96C8"/>
+</linearGradient>
+<linearGradient id="paint1_linear_34411_836" x1="20.9956" y1="25.4742" x2="23.56" y2="33.7692" gradientUnits="userSpaceOnUse">
+<stop stop-color="#EFF7FC"/>
+<stop offset="1" stop-color="white"/>
+</linearGradient>
+</defs>
+</svg>
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 512 512"><path d="M256 0c141.385 0 256 114.615 256 256S397.385 512 256 512 0 397.385 0 256 114.615 0 256 0z"/><path fill="#fff" fill-rule="nonzero" d="M318.64 157.549h33.401l-72.973 83.407 85.85 113.495h-67.222l-52.647-68.836-60.242 68.836h-33.423l78.052-89.212-82.354-107.69h68.924l47.59 62.917 55.044-62.917zm-11.724 176.908h18.51L205.95 176.493h-19.86l120.826 157.964z"/></svg>
--- /dev/null
+function openShareUrl(url, initialWidth = 640, initialHeight = 480) {
+ const width = Math.max(100, Math.min(screen.width, initialWidth));
+ const height = Math.max(100, Math.min(screen.height, initialHeight));
+
+ const left = (screen.width / 2) - (width / 2);
+ const top = (screen.height * 0.3) - (height / 2);
+ const opts = `width=${width},height=${height},left=${left},top=${top},menubar=no,status=no,location=no`;
+
+ window.open(url, "popup", opts);
+}
+
+$(document).ready(function () {
+ $(".ssb-icon").on("click", function (e) {
+ const shareUrl = $(this).attr("href");
+ if (!shareUrl.startsWith("mailto:")) {
+ e.preventDefault();
+ openShareUrl(shareUrl);
+ }
+ });
+});
+
module ApplicationHelper
require "rexml/document"
+ include SocialShareButtonHelper
def linkify(text)
if text.html_safe?
rescue StandardError
flash.inspect if Rails.env.development?
end
+
+ # Generates a set of social share buttons based on the specified options.
+ def render_social_share_buttons(opts = {})
+ sites = opts.fetch(:allow_sites, [])
+ valid_sites, invalid_sites = SocialShareButtonHelper.filter_allowed_sites(sites)
+
+ # Log invalid sites
+ invalid_sites.each do |invalid_site|
+ Rails.logger.error("Invalid site or icon not configured: #{invalid_site}")
+ end
+
+ tag.div(
+ :class => "social-share-button d-flex gap-1 align-items-end flex-wrap mb-3"
+ ) do
+ valid_sites.map do |site|
+ link_options = {
+ :rel => ["nofollow", opts[:rel]].compact,
+ :class => "ssb-icon rounded-circle",
+ :title => I18n.t("application.share.#{site}.title"),
+ :target => "_blank"
+ }
+
+ link_to SocialShareButtonHelper.generate_share_url(site, opts), link_options do
+ image_tag(SocialShareButtonHelper.icon_path(site), :alt => I18n.t("application.share.#{site}.alt"), :size => 28)
+ end
+ end.join.html_safe
+ end
+ end
end
<% end %>
</ul>
</nav>
+
</article>
+<% content_for :head do %>
+ <%= javascript_include_tag "social_share_button" %>
+<% end %>
+
<% content_for :heading do %>
<div class="row">
<div class="col-sm-auto">
<% end %>
<%= render @entry %>
+<%= render_social_share_buttons({
+ :title => @entry.title,
+ :url => diary_entry_url(@entry.user, @entry)
+ }) %>
<div id="comments" class="comments mb-3 overflow-hidden">
<div class="row border-bottom border-secondary-subtle">
wikipedia:
title: Log in with Wikipedia
alt: Wikipedia logo
+ share:
+ email:
+ title: Share via Email
+ alt: Email icon
+ facebook:
+ title: Share via Facebook
+ alt: Facebook Icon
+ linkedin:
+ title: Share via LinkedIn
+ alt: LinkedIn Icon
+ mastodon:
+ title: Share on Mastodon
+ alt: Mastodon Icon
+ telegram:
+ title: Share on Telegram
+ alt: Telegram Icon
+ x:
+ title: Share on X
+ alt: X Icon
oauth:
permissions:
missing: "You have not permitted the application access to this facility"
--- /dev/null
+module SocialShareButtonHelper
+ require "uri"
+
+ SOCIAL_SHARE_CONFIG = {
+ :email => "social_icons/email.svg",
+ :facebook => "social_icons/facebook.svg",
+ :linkedin => "social_icons/linkedin.svg",
+ :mastodon => "social_icons/mastodon.svg",
+ :telegram => "social_icons/telegram.svg",
+ :x => "social_icons/x.svg"
+ }.freeze
+
+ def self.filter_allowed_sites(sites)
+ valid_sites = sites.empty? ? SOCIAL_SHARE_CONFIG.keys : sites.select { |site| valid_site?(site) }
+ invalid_sites = sites - valid_sites
+ [valid_sites, invalid_sites]
+ end
+
+ def self.icon_path(site)
+ SOCIAL_SHARE_CONFIG[site.to_sym] || ""
+ end
+
+ def self.valid_site?(site)
+ SOCIAL_SHARE_CONFIG.key?(site.to_sym)
+ end
+
+ def self.generate_share_url(site, params)
+ site = site.to_sym
+ case site
+ when :email
+ to = params[:to] || ""
+ subject = CGI.escape(params[:title])
+ body = CGI.escape(params[:url])
+ "mailto:#{to}?subject=#{subject}&body=#{body}"
+ when :x
+ via_str = params[:via] ? "&via=#{URI.encode_www_form_component(params[:via])}" : ""
+ hashtags_str = params[:hashtags] ? "&hashtags=#{URI.encode_www_form_component(params[:hashtags].join(','))}" : ""
+ "https://x.com/intent/tweet?url=#{URI.encode_www_form_component(params[:url])}&text=#{URI.encode_www_form_component(params[:title])}#{hashtags_str}#{via_str}"
+ when :linkedin
+ "https://www.linkedin.com/sharing/share-offsite/?url=#{URI.encode_www_form_component(params[:url])}"
+ when :facebook
+ "https://www.facebook.com/sharer/sharer.php?u=#{URI.encode_www_form_component('params[:url]')}&t=#{URI.encode_www_form_component(params[:title])}"
+ when :mastodon
+ "https://mastodonshare.com/?text=#{URI.encode_www_form_component(params[:title])}&url=#{URI.encode_www_form_component(params[:url])}"
+ when :telegram
+ "https://t.me/share/url?url=#{URI.encode_www_form_component(params[:url])}&text=#{URI.encode_www_form_component(params[:title])}"
+ else
+ raise ArgumentError, "Unsupported platform: #{platform}"
+ end
+ end
+end
--- /dev/null
+require "test_helper"
+
+class SocialShareButtonHelperTest < ActionView::TestCase
+ include SocialShareButtonHelper
+ include ApplicationHelper
+
+ def setup
+ @options = {
+ :allow_sites => %w[x facebook linkedin],
+ :title => "Test Title",
+ :url => "https://example.com",
+ :desc => "Test Description",
+ :via => "testuser"
+ }
+ end
+
+ def test_render_social_share_buttons_with_valid_sites
+ result = render_social_share_buttons(@options)
+ assert_includes result, "x"
+ assert_includes result, "facebook"
+ assert_includes result, "linkedin"
+ end
+
+ def test_render_social_share_buttons_with_invalid_site
+ @options[:allow_sites] << "invalid_site"
+ result = render_social_share_buttons(@options)
+ assert_not_includes result, "invalid_site"
+ end
+
+ def test_render_social_share_buttons_with_no_sites
+ @options[:allow_sites] = []
+ result = render_social_share_buttons(@options)
+ SocialShareButtonHelper::SOCIAL_SHARE_CONFIG.each_key do |site|
+ assert_includes result, site.to_s # Convert symbol to string
+ end
+ end
+
+ def test_filter_allowed_sites
+ valid_sites, invalid_sites = SocialShareButtonHelper.filter_allowed_sites(%w[x facebook invalid_site])
+ assert_equal %w[x facebook], valid_sites
+ assert_equal %w[invalid_site], invalid_sites
+ end
+
+ def test_icon_path
+ assert_equal "social_icons/x.svg", SocialShareButtonHelper.icon_path("x")
+ assert_equal "", SocialShareButtonHelper.icon_path("invalid_site")
+ end
+end