Extract message system to plugin.

This commit is contained in:
Robert Waltemath 2014-03-07 09:51:24 +01:00 committed by wvengen
parent fe0b17cdb0
commit 7556c753d0
45 changed files with 298 additions and 45 deletions

View file

@ -44,6 +44,7 @@ gem 'ruby-units'
# we use the git version of acts_as_versioned, and need to include it in this Gemfile # we use the git version of acts_as_versioned, and need to include it in this Gemfile
gem 'acts_as_versioned', git: 'git://github.com/technoweenie/acts_as_versioned.git' gem 'acts_as_versioned', git: 'git://github.com/technoweenie/acts_as_versioned.git'
gem 'foodsoft_wiki', path: 'lib/foodsoft_wiki' gem 'foodsoft_wiki', path: 'lib/foodsoft_wiki'
gem 'foodsoft_messages', path: 'lib/foodsoft_messages'
group :production do group :production do
gem 'exception_notification' gem 'exception_notification'

View file

@ -41,6 +41,13 @@ GIT
acts_as_versioned (0.6.0) acts_as_versioned (0.6.0)
activerecord (>= 3.0.9) activerecord (>= 3.0.9)
PATH
remote: lib/foodsoft_messages
specs:
foodsoft_messages (0.0.1)
deface (~> 1.0.0)
rails
PATH PATH
remote: lib/foodsoft_wiki remote: lib/foodsoft_wiki
specs: specs:
@ -120,6 +127,7 @@ GEM
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.7.0) coffee-script-source (1.7.0)
colorize (0.6.0)
commonjs (0.2.7) commonjs (0.2.7)
connection_pool (1.2.0) connection_pool (1.2.0)
content_for_in_controllers (0.0.2) content_for_in_controllers (0.0.2)
@ -132,6 +140,10 @@ GEM
daemons (1.1.9) daemons (1.1.9)
database_cleaner (1.2.0) database_cleaner (1.2.0)
debug_inspector (0.0.2) debug_inspector (0.0.2)
deface (1.0.0)
colorize (>= 0.5.8)
nokogiri (~> 1.6.0)
rails (>= 3.1)
diff-lcs (1.2.5) diff-lcs (1.2.5)
diffy (3.0.1) diffy (3.0.1)
docile (1.1.3) docile (1.1.3)
@ -397,6 +409,7 @@ DEPENDENCIES
exception_notification exception_notification
factory_girl_rails (~> 4.0) factory_girl_rails (~> 4.0)
faker faker
foodsoft_messages!
foodsoft_wiki! foodsoft_wiki!
haml-rails haml-rails
i18n-js! i18n-js!

View file

@ -199,14 +199,9 @@ module ApplicationHelper
end end
end end
# render user presentation linking to default action (write message) # render user presentation linking to default action (plugins can override this)
def show_user_link(user=@current_user) def show_user_link(user=@current_user)
if user.nil?
show_user user show_user user
else
link_to show_user(user), new_message_path('message[mail_to]' => user.id),
:title => I18n.t('helpers.application.write_message')
end
end end
end end

View file

@ -13,16 +13,6 @@ class Mailer < ActionMailer::Base
sender: FoodsoftConfig[:email_sender], sender: FoodsoftConfig[:email_sender],
errors_to: FoodsoftConfig[:email_sender] errors_to: FoodsoftConfig[:email_sender]
# Sends an email copy of the given internal foodsoft message.
def foodsoft_message(message, recipient)
set_foodcoop_scope
@message = message
mail subject: "[#{FoodsoftConfig[:name]}] " + message.subject,
to: recipient.email,
from: "#{show_user(message.sender)} <#{message.sender.email}>"
end
# Sends an email with instructions on how to reset the password. # Sends an email with instructions on how to reset the password.
# Assumes user.setResetPasswordToken has been successfully called already. # Assumes user.setResetPasswordToken has been successfully called already.
def reset_password(user) def reset_password(user)

View file

@ -3,4 +3,3 @@
%section= render 'shared/group', group: @ordergroup %section= render 'shared/group', group: @ordergroup
= link_to t('ui.edit'), edit_admin_ordergroup_path(@ordergroup), class: 'btn' = link_to t('ui.edit'), edit_admin_ordergroup_path(@ordergroup), class: 'btn'
= link_to t('ui.delete'), [:admin, @ordergroup], :data => {:confirm => t('.confirm')}, :method => :delete, class: 'btn btn-danger' = link_to t('ui.delete'), [:admin, @ordergroup], :data => {:confirm => t('.confirm')}, :method => :delete, class: 'btn btn-danger'
= link_to t('.send_message'), new_message_path(:message => {:group_id => @ordergroup.id}), class: 'btn'

View file

@ -37,4 +37,3 @@
= link_to t('ui.edit'), edit_admin_user_path(@user), class: 'btn' = link_to t('ui.edit'), edit_admin_user_path(@user), class: 'btn'
= link_to t('ui.delete'), [:admin, @user], :data => {:confirm => t('.confirm', user: @user.first_name)}, = link_to t('ui.delete'), [:admin, @user], :data => {:confirm => t('.confirm', user: @user.first_name)},
:method => :delete, class: 'btn btn-danger' :method => :delete, class: 'btn btn-danger'
= link_to t('.send_message'), new_message_path(:message => {:mail_to => @user.id}), class: 'btn'

View file

@ -3,4 +3,3 @@
%section= render 'shared/group', group: @workgroup %section= render 'shared/group', group: @workgroup
= link_to t('ui.edit'), edit_admin_workgroup_path(@workgroup), class: 'btn' = link_to t('ui.edit'), edit_admin_workgroup_path(@workgroup), class: 'btn'
= link_to t('ui.delete'), [:admin, @workgroup], :data => {:confirm => t('.confirm')}, :method => :delete, class: 'btn btn-danger' = link_to t('ui.delete'), [:admin, @workgroup], :data => {:confirm => t('.confirm')}, :method => :delete, class: 'btn btn-danger'
= link_to_new_message(message_params: {group_id: @workgroup.id})

View file

@ -15,5 +15,4 @@
%td= ordergroup.name %td= ordergroup.name
%td=h ordergroup.users.collect { |u| show_user(u) }.join(", ") %td=h ordergroup.users.collect { |u| show_user(u) }.join(", ")
%td= format_date ordergroup.last_order.try(:starts) %td= format_date ordergroup.last_order.try(:starts)
%td= link_to_new_message(message_params: {group_id: ordergroup.id})

View file

@ -21,5 +21,4 @@
%td= user.phone if @current_user.role_admin? || user.settings.profile["phone_is_public"] %td= user.phone if @current_user.role_admin? || user.settings.profile["phone_is_public"]
%td= user.ordergroup_name %td= user.ordergroup_name
%td= user.workgroups.collect(&:name).join(', ') %td= user.workgroups.collect(&:name).join(', ')
%td= link_to_new_message(message_params: {mail_to: user.id})

View file

@ -2,6 +2,5 @@
%h3= workgroup.name %h3= workgroup.name
= render :partial => 'shared/group', :locals => { :group => workgroup } = render :partial => 'shared/group', :locals => { :group => workgroup }
= link_to t('.show_tasks'), workgroup_tasks_path(workgroup_id: workgroup), class: 'btn' = link_to t('.show_tasks'), workgroup_tasks_path(workgroup_id: workgroup), class: 'btn'
= link_to_new_message message_params: {group_id: workgroup.id}
- if workgroup.member?(current_user) - if workgroup.member?(current_user)
= link_to t('.edit'), edit_foodcoop_workgroup_path(workgroup), class: 'btn' = link_to t('.edit'), edit_foodcoop_workgroup_path(workgroup), class: 'btn'

View file

@ -4,7 +4,6 @@
%li.nav-header= t '.foodcoop' %li.nav-header= t '.foodcoop'
%li= link_to t('.members'), foodcoop_users_path %li= link_to t('.members'), foodcoop_users_path
%li= link_to t('.tasks'), user_tasks_path %li= link_to t('.tasks'), user_tasks_path
%li= link_to t('.write_message'), new_message_path
- has_ordergroup = !@current_user.ordergroup.nil? - has_ordergroup = !@current_user.ordergroup.nil?
- has_orders_role = @current_user.role_orders? - has_orders_role = @current_user.role_orders?

View file

@ -3,6 +3,9 @@
- content_for(:sidebar) do - content_for(:sidebar) do
= render :partial => 'start_nav' = render :partial => 'start_nav'
-# placeholder deface to add content using erb[silent]:contains()
- '<dashboard_top_mark>'
- unless @unaccepted_tasks.empty? && @next_tasks.empty? && @unassigned_tasks.size == 0 - unless @unaccepted_tasks.empty? && @next_tasks.empty? && @unassigned_tasks.size == 0
%section.row-fluid %section.row-fluid
- unless @next_tasks.empty? - unless @next_tasks.empty?
@ -32,11 +35,8 @@
%h2= t '.ordergroup.title' %h2= t '.ordergroup.title'
= render :partial => "apple_bar", :locals => {:apple_bar => AppleBar.new(current_user.ordergroup)} = render :partial => "apple_bar", :locals => {:apple_bar => AppleBar.new(current_user.ordergroup)}
- unless Message.public.empty? -# placeholder deface to add content using erb[silent]:contains()
%section - '<dashboard_middle_mark>'
%h2= t '.messages.title'
= render 'messages/messages', messages: Message.public.order('created_at DESC').limit(5), pagination: false
%p= link_to t('.messages.view_all'), messages_path
- if current_user.ordergroup - if current_user.ordergroup
// Ordergroup overview // Ordergroup overview
@ -63,3 +63,6 @@
%td{:style => "color:#{color}; width:5em", :class => "currency"}= number_to_currency(ft.amount) %td{:style => "color:#{color}; width:5em", :class => "currency"}= number_to_currency(ft.amount)
%br/ %br/
%p= link_to t('.my_ordergroup.transactions.view'), my_ordergroup_path %p= link_to t('.my_ordergroup.transactions.view'), my_ordergroup_path
-# placeholder deface to add content using erb[silent]:contains()
- '<dashboard_bottom_mark>'

View file

@ -8,11 +8,6 @@ class UserNotifier
self.send method_name, args self.send method_name, args
end end
def self.message_deliver(args)
message_id = args.first
Message.find(message_id).deliver
end
def self.finished_order(args) def self.finished_order(args)
order_id = args.first order_id = args.first
Order.find(order_id).group_orders.each do |group_order| Order.find(order_id).group_orders.each do |group_order|

View file

@ -748,10 +748,11 @@ en:
role_suppliers: Suppliers role_suppliers: Suppliers
show_google_maps: Show it on Google maps show_google_maps: Show it on Google maps
sort_by: Sort by %{text} sort_by: Sort by %{text}
write_message: Write message
deliveries: deliveries:
new_invoice: New invoice new_invoice: New invoice
show_invoice: Show invoice show_invoice: Show invoice
messages:
write_message: Write message
orders: orders:
old_price: Old price old_price: Old price
option_choose: Choose supplier/stock option_choose: Choose supplier/stock
@ -896,11 +897,6 @@ en:
feedback: feedback:
header: '%{user} wrote at %{date}:' header: '%{user} wrote at %{date}:'
subject: Feedback from %{email} subject: Feedback from %{email}
foodsoft_message:
footer: |
Reply: %{reply_url}
See message online: %{msg_url}
Messaging options: %{profile_url}
invite: invite:
subject: Invitation to the Foodcoop subject: Invitation to the Foodcoop
text: | text: |
@ -1007,6 +1003,12 @@ en:
sent_on: 'Sent:' sent_on: 'Sent:'
subject: 'Subject:' subject: 'Subject:'
title: Show message title: Show message
messages_mailer:
foodsoft_message:
footer: |
Reply: %{reply_url}
See message online: %{msg_url}
Messaging options: %{profile_url}
model: model:
delivery: delivery:
each_stock_article_must_be_unique: Each stock article must not be listed more than once. each_stock_article_must_be_unique: Each stock article must not be listed more than once.

View file

@ -17,7 +17,6 @@ SimpleNavigation::Configuration.run do |navigation|
subnav.item :members, I18n.t('navigation.members'), foodcoop_users_path, id: nil subnav.item :members, I18n.t('navigation.members'), foodcoop_users_path, id: nil
subnav.item :workgroups, I18n.t('navigation.workgroups'), foodcoop_workgroups_path, id: nil subnav.item :workgroups, I18n.t('navigation.workgroups'), foodcoop_workgroups_path, id: nil
subnav.item :ordergroups, I18n.t('navigation.ordergroups'), foodcoop_ordergroups_path, id: nil subnav.item :ordergroups, I18n.t('navigation.ordergroups'), foodcoop_ordergroups_path, id: nil
subnav.item :messages, I18n.t('navigation.messages'), messages_path, id: nil
subnav.item :tasks, I18n.t('navigation.tasks'), tasks_path, id: nil subnav.item :tasks, I18n.t('navigation.tasks'), tasks_path, id: nil
end end

View file

@ -73,8 +73,6 @@ Foodsoft::Application.routes.draw do
end end
end end
resources :messages, :only => [:index, :show, :new, :create]
namespace :foodcoop do namespace :foodcoop do
root :to => 'users#index' root :to => 'users#index'

View file

@ -0,0 +1,15 @@
FoodsoftMessages
================
This plugin adds messages to foodsoft. A new 'Messages' menu entry is added below the 'Foodcoops' menu in the navigation bar.
This plugin is enabled by default in foodsoft, so you don't need to do anything
to install it. If you still want to, for example when it has been disabled,
add the following to foodsoft's Gemfile:
```Gemfile
gem 'foodsoft_messages', path: 'lib/foodsoft_messages'
```
This plugin is part of the foodsoft package and uses the GPL-3 license (see
foodsoft's LICENSE for the full license text).

View file

@ -0,0 +1,40 @@
#!/usr/bin/env rake
begin
require 'bundler/setup'
rescue LoadError
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
end
begin
require 'rdoc/task'
rescue LoadError
require 'rdoc/rdoc'
require 'rake/rdoctask'
RDoc::Task = Rake::RDocTask
end
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'FoodsoftMessages'
rdoc.options << '--line-numbers'
rdoc.rdoc_files.include('README.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
end
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
Bundler::GemHelper.install_tasks
require 'rake/testtask'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = false
end
task :default => :test

View file

@ -17,7 +17,7 @@ class MessagesController < ApplicationController
def create def create
@message = @current_user.send_messages.new(params[:message]) @message = @current_user.send_messages.new(params[:message])
if @message.save if @message.save
Resque.enqueue(UserNotifier, FoodsoftConfig.scope, 'message_deliver', @message.id) Resque.enqueue(MessageNotifier, FoodsoftConfig.scope, 'message_deliver', @message.id)
redirect_to messages_url, :notice => I18n.t('messages.create.notice') redirect_to messages_url, :notice => I18n.t('messages.create.notice')
else else
render :action => 'new' render :action => 'new'

View file

@ -17,4 +17,5 @@ module MessagesHelper
link_to(link_text.html_safe, new_message_path(message: messages_params), class: 'btn', link_to(link_text.html_safe, new_message_path(message: messages_params), class: 'btn',
title: I18n.t('helpers.submit.message.create')) title: I18n.t('helpers.submit.message.create'))
end end
end end

View file

@ -0,0 +1,11 @@
class MessagesMailer < Mailer
# Sends an email copy of the given internal foodsoft message.
def foodsoft_message(message, recipient)
set_foodcoop_scope
@message = message
mail subject: "[#{FoodsoftConfig[:name]}] " + message.subject,
to: recipient.email,
from: "#{show_user(message.sender)} <#{message.sender.email}>"
end
end

View file

@ -75,7 +75,7 @@ class Message < ActiveRecord::Base
for user in recipients for user in recipients
if user.receive_email? if user.receive_email?
begin begin
Mailer.foodsoft_message(self, user).deliver MessagesMailer.foodsoft_message(self, user).deliver
rescue rescue
Rails.logger.warn "Deliver failed for user \##{user.id}: #{user.email}" Rails.logger.warn "Deliver failed for user \##{user.id}: #{user.email}"
end end

View file

@ -0,0 +1,2 @@
/ insert_after 'erb:contains("delete")'
= link_to t('.send_message'), new_message_path(:message => {:group_id => @ordergroup.id}), class: 'btn'

View file

@ -0,0 +1,2 @@
/ insert_after 'erb:contains("delete")'
= link_to t('.send_message'), new_message_path(:message => {:mail_to => @user.id}), class: 'btn'

View file

@ -0,0 +1,2 @@
/ insert_after 'erb:contains("delete")'
= link_to_new_message(message_params: {group_id: @workgroup.id})

View file

@ -0,0 +1,2 @@
/ insert_bottom 'tbody tr'
%td= link_to_new_message(message_params: {group_id: ordergroup.id})

View file

@ -0,0 +1,2 @@
/ insert_bottom 'tbody tr'
%td= link_to_new_message(message_params: {mail_to: user.id})

View file

@ -0,0 +1,2 @@
/ insert_after 'erb:contains("tasks")'
= link_to_new_message message_params: {group_id: workgroup.id}

View file

@ -0,0 +1,2 @@
/ insert_after 'erb:contains("tasks")'
%li= link_to t('.write_message'), new_message_path

View file

@ -0,0 +1,6 @@
/ insert_after 'erb[silent]:contains("<dashboard_middle_mark>")'
- unless Message.public.empty?
%section#messages
%h2= t '.messages.title'
= render 'messages/messages', messages: Message.public.order('created_at DESC').limit(5), pagination: false
%p= link_to t('.messages.view_all'), messages_path

View file

@ -0,0 +1,8 @@
class MessageNotifier < UserNotifier
@queue = :foodsoft_notifier
def self.message_deliver(args)
message_id = args.first
Message.find(message_id).deliver
end
end

View file

@ -0,0 +1,77 @@
en:
activerecord:
attributes:
message:
body: Body
group_id: Group
private: Private
recipient_tokens: Recipients
sent_to_all: Send to all members
subject: Subject
models:
message: Message
admin:
ordergroups:
show:
send_message: Send message
users:
show:
send_message: Send message
helpers:
messages:
write_message: Write message
submit:
message:
create: send message
home:
index:
messages:
title: Newest Messages
view_all: See all messages
start_nav:
write_message: Write message
messages:
create:
notice: Message is saved and will be sent.
index:
new: New message
title: Messages
messages:
reply: Reply
model:
reply_header: ! '%{user} wrote on %{when}:'
reply_indent: ! '> %{line}'
reply_subject: ! 'Re: %{subject}'
new:
list:
desc: ! 'Please send messages to all using the mailing-list: %{list}'
mail: for example with an email to %{email}.
subscribe: You can find more about the mailing-list at %{link}.
subscribe_msg: You may have to subscribe to the mailing-list first.
wiki: Wiki (page Mailing-List)
no_user_found: No user found
search: Search ...
search_user: Search user
title: New message
show:
all_messages: All messages
from: ! 'From:'
reply: Reply
sent_on: ! 'Sent:'
subject: ! 'Subject:'
title: Show message
messages_mailer:
foodsoft_message:
footer: ! 'Reply: %{reply_url}
See message online: %{msg_url}
Messaging options: %{profile_url}
'
navigation:
messages: Messages
simple_form:
hints:
message:
private: Message doesnt show in Foodsoft mail inbox

View file

@ -0,0 +1,5 @@
Rails.application.routes.draw do
scope '/:foodcoop' do
resources :messages, :only => [:index, :show, :new, :create]
end
end

View file

@ -0,0 +1,18 @@
# extracted from 20090120184410_road_to_version_three.rb
class CreateMessages < ActiveRecord::Migration
def self.up
create_table :messages do |t|
t.references :sender
t.text :recipients_ids
t.string :subject, :null => false
t.text :body
t.integer :email_state, :default => 0, :null => false
t.boolean :private, :default => false
t.datetime :created_at
end
end
def self.down
drop_table :messages
end
end

View file

@ -0,0 +1,23 @@
$:.push File.expand_path("../lib", __FILE__)
# Maintain your gem's version:
require "foodsoft_messages/version"
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = "foodsoft_messages"
s.version = FoodsoftMessages::VERSION
s.authors = ["robwa"]
s.email = ["foodsoft-messages@ini.tiative.net"]
s.homepage = "https://github.com/foodcoops/foodsoft"
s.summary = "Messaging plugin for foodsoft."
s.description = "Adds the ability to exchange messages to foodsoft."
s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"]
s.test_files = Dir["test/**/*"]
s.add_dependency "rails"
s.add_dependency "deface", "~> 1.0.0"
s.add_development_dependency "sqlite3"
end

View file

@ -0,0 +1,6 @@
require "foodsoft_messages/engine"
require "foodsoft_messages/user_link"
require "deface"
module FoodsoftMessages
end

View file

@ -0,0 +1,11 @@
module FoodsoftMessages
class Engine < ::Rails::Engine
def navigation(primary, context)
item = SimpleNavigation::Item.new(primary, :messages, I18n.t('navigation.messages'), context.messages_path)
sub_nav = primary[:foodcoop].sub_navigation
# display right before tasks item
tasks_index = sub_nav.items.index(sub_nav[:tasks])
sub_nav.items.insert(tasks_index, item)
end
end
end

View file

@ -0,0 +1,26 @@
module FoodsoftMessages
module UserLink
def self.included(base) # :nodoc:
base.class_eval do
# modify user presentation link to writing a message for the user
def show_user_link(user=@current_user)
if user.nil?
show_user user
else
link_to show_user(user), new_message_path('message[mail_to]' => user.id),
:title => I18n.t('helpers.messages.write_message')
end
end
end
end
end
end
# modify existing helper
ActiveSupport.on_load(:after_initialize) do
ApplicationHelper.send :include, FoodsoftMessages::UserLink
end

View file

@ -0,0 +1,3 @@
module FoodsoftMessages
VERSION = "0.0.1"
end