feat(messages): add html formatting to messages
This commit allows users to use the trix editor to send messages with basic formatting and attachements. * add active storage * add actiontext * add richtext field to messages * add imageprocessing for message attachements * add html email layout and adjust translations to use html urls
This commit is contained in:
parent
206639cf41
commit
c8e0c79114
27 changed files with 148 additions and 7 deletions
1
Gemfile
1
Gemfile
|
@ -129,3 +129,4 @@ end
|
|||
gem "importmap-rails", "~> 1.1"
|
||||
|
||||
gem "terser", "~> 1.1"
|
||||
gem "image_processing", "~> 1.12"
|
||||
|
|
|
@ -254,6 +254,9 @@ GEM
|
|||
i18n-spec (0.6.0)
|
||||
iso
|
||||
ice_cube (0.16.4)
|
||||
image_processing (1.12.2)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
ruby-vips (>= 2.0.17, < 3)
|
||||
importmap-rails (1.1.5)
|
||||
actionpack (>= 6.0.0)
|
||||
railties (>= 6.0.0)
|
||||
|
@ -317,6 +320,7 @@ GEM
|
|||
mime-types (3.4.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2022.0105)
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.2)
|
||||
minitest (5.17.0)
|
||||
mono_logger (1.1.1)
|
||||
|
@ -495,6 +499,8 @@ GEM
|
|||
ruby-prof (1.4.5)
|
||||
ruby-progressbar (1.11.0)
|
||||
ruby-units (3.0.0)
|
||||
ruby-vips (2.1.4)
|
||||
ffi (~> 1.12)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
sass-rails (6.0.0)
|
||||
|
@ -630,6 +636,7 @@ DEPENDENCIES
|
|||
i18n-js (~> 3.0.0.rc8)
|
||||
i18n-spec
|
||||
ice_cube
|
||||
image_processing (~> 1.12)
|
||||
importmap-rails (~> 1.1)
|
||||
inherited_resources
|
||||
jquery-rails
|
||||
|
|
31
app/assets/stylesheets/actiontext.css
Normal file
31
app/assets/stylesheets/actiontext.css
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and
|
||||
* the trix-editor content (whether displayed or under editing). Feel free to incorporate this
|
||||
* inclusion directly in any other asset bundle and remove this file.
|
||||
*
|
||||
*= require trix
|
||||
*/
|
||||
|
||||
/*
|
||||
* We need to override trix.css’s image gallery styles to accommodate the
|
||||
* <action-text-attachment> element we wrap around attachments. Otherwise,
|
||||
* images in galleries will be squished by the max-width: 33%; rule.
|
||||
*/
|
||||
.trix-content .attachment-gallery > action-text-attachment,
|
||||
.trix-content .attachment-gallery > .attachment {
|
||||
flex: 1 0 33%;
|
||||
padding: 0 0.5em;
|
||||
max-width: 33%;
|
||||
}
|
||||
|
||||
.trix-content .attachment-gallery.attachment-gallery--2 > action-text-attachment,
|
||||
.trix-content .attachment-gallery.attachment-gallery--2 > .attachment, .trix-content .attachment-gallery.attachment-gallery--4 > action-text-attachment,
|
||||
.trix-content .attachment-gallery.attachment-gallery--4 > .attachment {
|
||||
flex-basis: 50%;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.trix-content action-text-attachment .attachment {
|
||||
padding: 0 !important;
|
||||
max-width: 100% !important;
|
||||
}
|
|
@ -7,4 +7,5 @@
|
|||
*= require list.unlist
|
||||
*= require list.missing
|
||||
*= require recurring_select
|
||||
*= require actiontext
|
||||
*/
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
||||
import "trix"
|
||||
import "@rails/actiontext"
|
||||
|
|
14
app/views/active_storage/blobs/_blob.html.erb
Normal file
14
app/views/active_storage/blobs/_blob.html.erb
Normal file
|
@ -0,0 +1,14 @@
|
|||
<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
|
||||
<% if blob.representable? %>
|
||||
<%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
|
||||
<% end %>
|
||||
|
||||
<figcaption class="attachment__caption">
|
||||
<% if caption = blob.try(:caption) %>
|
||||
<%= caption %>
|
||||
<% else %>
|
||||
<span class="attachment__name"><%= blob.filename %></span>
|
||||
<span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
|
||||
<% end %>
|
||||
</figcaption>
|
||||
</figure>
|
3
app/views/layouts/action_text/contents/_content.html.erb
Normal file
3
app/views/layouts/action_text/contents/_content.html.erb
Normal file
|
@ -0,0 +1,3 @@
|
|||
<div class="trix-content">
|
||||
<%= yield -%>
|
||||
</div>
|
12
app/views/layouts/email.html.haml
Normal file
12
app/views/layouts/email.html.haml
Normal file
|
@ -0,0 +1,12 @@
|
|||
= yield
|
||||
\
|
||||
%hr
|
||||
%ul
|
||||
%li
|
||||
%a{href: root_url} Foodsoft
|
||||
- if FoodsoftConfig[:homepage]
|
||||
%li
|
||||
%a{href: FoodsoftConfig[:homepage]} Foodcoop
|
||||
- if FoodsoftConfig[:help_url]
|
||||
%li
|
||||
%a{href: FoodsoftConfig[:help_url]}= t '.help'
|
|
@ -67,6 +67,8 @@ module Foodsoft
|
|||
|
||||
config.autoloader = :zeitwerk
|
||||
|
||||
config.active_storage.variant_processor = :mini_magick
|
||||
|
||||
# Ex:- :default =>''
|
||||
|
||||
# CORS for API
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
# Pin npm packages by running ./bin/importmap
|
||||
pin "application", preload: true
|
||||
pin "application", preload: true
|
||||
pin "trix"
|
||||
pin "@rails/actiontext", to: "actiontext.js"
|
||||
|
|
|
@ -1222,6 +1222,7 @@ de:
|
|||
footer_2_foodsoft: 'Foodsoft: %{url}'
|
||||
footer_3_homepage: 'Foodcoop: %{url}'
|
||||
footer_4_help: 'Hilfe: %{url}'
|
||||
help: 'Hilfe'
|
||||
foodsoft: Foodsoft
|
||||
footer:
|
||||
revision: Revision %{revision}
|
||||
|
|
|
@ -1225,6 +1225,7 @@ en:
|
|||
footer_2_foodsoft: 'Foodsoft: %{url}'
|
||||
footer_3_homepage: 'Foodcoop: %{url}'
|
||||
footer_4_help: 'Help: %{url}'
|
||||
help: 'Help'
|
||||
foodsoft: Foodsoft
|
||||
footer:
|
||||
revision: revision %{revision}
|
||||
|
|
|
@ -1083,6 +1083,7 @@ es:
|
|||
layouts:
|
||||
email:
|
||||
footer_4_help: 'Ayuda: %{url}'
|
||||
help: 'Ayuda'
|
||||
footer:
|
||||
revision: revisión %{revision}
|
||||
header:
|
||||
|
|
|
@ -834,6 +834,7 @@ fr:
|
|||
email:
|
||||
footer_3_homepage: 'Boufcoop: %{url}'
|
||||
footer_4_help: 'Aide: %{url}'
|
||||
help: 'Aide'
|
||||
footer:
|
||||
revision: révision %{revision}
|
||||
header:
|
||||
|
|
|
@ -1195,6 +1195,7 @@ nl:
|
|||
footer_2_foodsoft: 'Foodsoft: %{url}'
|
||||
footer_3_homepage: 'Foodcoop: %{url}'
|
||||
footer_4_help: 'Help: %{url}'
|
||||
help: 'Help'
|
||||
foodsoft: Foodsoft
|
||||
footer:
|
||||
revision: revisie %{revision}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# This migration comes from action_text (originally 20180528164100)
|
||||
class CreateActionTextTables < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
# Use Active Record's configured type for primary and foreign keys
|
||||
primary_key_type, foreign_key_type = primary_and_foreign_key_types
|
||||
|
||||
create_table :action_text_rich_texts, id: primary_key_type do |t|
|
||||
t.string :name, null: false
|
||||
t.text :body, size: :long
|
||||
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
|
||||
|
||||
t.timestamps
|
||||
|
||||
t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def primary_and_foreign_key_types
|
||||
config = Rails.configuration.generators
|
||||
setting = config.options[config.orm][:primary_key_type]
|
||||
primary_key_type = setting || :primary_key
|
||||
foreign_key_type = setting || :bigint
|
||||
[primary_key_type, foreign_key_type]
|
||||
end
|
||||
end
|
12
db/schema.rb
12
db/schema.rb
|
@ -10,7 +10,17 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_01_06_144440) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2023_02_09_105256) do
|
||||
create_table "action_text_rich_texts", charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.text "body", size: :long
|
||||
t.string "record_type", null: false
|
||||
t.bigint "record_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
|
||||
end
|
||||
|
||||
create_table "active_storage_attachments", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.string "record_type", null: false
|
||||
|
|
|
@ -20,8 +20,8 @@ class MessagesController < ApplicationController
|
|||
@message.group_id = original_message.group_id
|
||||
@message.private = original_message.private
|
||||
@message.subject = I18n.t('messages.model.reply_subject', :subject => original_message.subject)
|
||||
@message.body = I18n.t('messages.model.reply_header', :user => original_message.sender.display, :when => I18n.l(original_message.created_at, :format => :short)) + "\n"
|
||||
original_message.body.each_line { |l| @message.body += I18n.t('messages.model.reply_indent', :line => l) }
|
||||
@message.body = I18n.t('messages.model.reply_header', :user => original_message.sender.display, :when => I18n.l(original_message.created_at, :format => :short)) + "\n" \
|
||||
+ "<blockquote>" + original_message.body.to_trix_html + "</blockquote>"
|
||||
else
|
||||
redirect_to new_message_url, alert: I18n.t('messages.new.error_private')
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ module MessagesHelper
|
|||
body = ""
|
||||
else
|
||||
subject = message.subject
|
||||
body = truncate(message.body, :length => length - subject.length)
|
||||
body = truncate(message.body.to_plain_text, :length => length - subject.length)
|
||||
end
|
||||
"<b>#{link_to(h(subject), message)}</b> <span style='color:grey'>#{h(body)}</span>".html_safe
|
||||
end
|
||||
|
|
|
@ -22,6 +22,8 @@ class Message < ApplicationRecord
|
|||
validates_presence_of :message_recipients, :subject, :body
|
||||
validates_length_of :subject, :in => 1..255
|
||||
|
||||
has_rich_text :body
|
||||
|
||||
after_initialize do
|
||||
@recipients_ids ||= []
|
||||
@send_method ||= 'recipients'
|
||||
|
|
|
@ -110,7 +110,7 @@
|
|||
= f.input :recipient_tokens, :input_html => { 'data-pre' => User.where(id: @message.recipients_ids).map(&:token_attributes).to_json }
|
||||
= f.input :private, inline_label: t('.hint_private')
|
||||
= f.input :subject, input_html: {class: 'input-xxlarge'}
|
||||
= f.input :body, input_html: {class: 'input-xxlarge', rows: 13}
|
||||
= f.rich_text_area :body, input_html: {class: 'input-xxlarge', rows: 13}
|
||||
.form-actions
|
||||
= f.submit class: 'btn btn-primary'
|
||||
= link_to t('ui.or_cancel'), :back
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
- if @message.can_toggle_private?(current_user)
|
||||
= link_to t('.change_visibility'), toggle_private_message_path(@message), method: :post, class: 'btn btn-mini'
|
||||
%hr/
|
||||
%p= simple_format(h(@message.body))
|
||||
.trix-content= @message.body
|
||||
%hr/
|
||||
%p
|
||||
= link_to t('.reply'), new_message_path(:message => {:reply_to => @message.id}), class: 'btn'
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
= raw @message.body
|
||||
%hr
|
||||
%ul
|
||||
- if @message.group
|
||||
%li= t '.footer_group', group: @message.group.name
|
||||
%li
|
||||
%a{href: new_message_url('message[reply_to]' => @message.id)}= t '.reply'
|
||||
%li
|
||||
%a{href: message_url(@message)}= t '.see_message_online'
|
||||
%li
|
||||
%a{href: my_profile_url}= t '.messaging_options'
|
|
@ -138,6 +138,9 @@ de:
|
|||
Antworten: %{reply_url}
|
||||
Nachricht online einsehen: %{msg_url}
|
||||
Nachrichten-Einstellungen: %{profile_url}
|
||||
reply: Antworten
|
||||
see_message_online: Nachricht online einsehen
|
||||
messaging_options: Nachrichten-Einstellungen
|
||||
footer_group: |
|
||||
Gesendet an Gruppe: %{group}
|
||||
navigation:
|
||||
|
|
|
@ -140,6 +140,9 @@ en:
|
|||
Reply: %{reply_url}
|
||||
See message online: %{msg_url}
|
||||
Messaging options: %{profile_url}
|
||||
reply: Reply
|
||||
see_message_online: See message online
|
||||
messaging_options: Messaging options
|
||||
footer_group: |
|
||||
Sent to group: %{group}
|
||||
navigation:
|
||||
|
|
|
@ -67,6 +67,9 @@ fr:
|
|||
Répondre: %{reply_url}
|
||||
Afficher ce message dans ton navigateur: %{msg_url}
|
||||
Préférences des messages: %{profile_url}
|
||||
reply: Répondre
|
||||
see_message_online: Afficher ce message dans ton navigateur
|
||||
messaging_options: Préférences des messages
|
||||
simple_form:
|
||||
labels:
|
||||
settings:
|
||||
|
|
|
@ -140,6 +140,9 @@ nl:
|
|||
Antwoorden: %{reply_url}
|
||||
Bericht online lezen: %{msg_url}
|
||||
Berichtinstellingen: %{profile_url}
|
||||
reply: Antwoorden
|
||||
see_message_online: Bericht online lezen
|
||||
messaging_options: Berichtinstellingen
|
||||
footer_group: |
|
||||
Verzenden aan groep: %{group}
|
||||
navigation:
|
||||
|
|
Loading…
Reference in a new issue