Move plugins to separate directory.

This commit is contained in:
wvengen 2014-11-22 00:43:24 +01:00
parent 6e990fed4c
commit 6aa8ee7968
59 changed files with 4 additions and 4 deletions

View file

@ -0,0 +1,18 @@
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 introduces the foodcoop config option `use_messages`, which can be
set to `false` to disable messages. May be useful in multicoop deployments.
This plugin is part of the foodsoft package and uses the GPL-3 license (see
foodsoft's LICENSE for the full license text).

40
plugins/messages/Rakefile Normal file
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

@ -0,0 +1,36 @@
class MessagesController < ApplicationController
before_filter -> { require_plugin_enabled FoodsoftMessages }
# Renders the "inbox" action.
def index
@messages = Message.pub.page(params[:page]).per(@per_page).order('created_at DESC').includes(:sender)
end
# Creates a new message object.
def new
@message = Message.new(params[:message])
if @message.reply_to and not @message.reply_to.is_readable_for?(current_user)
redirect_to new_message_url, alert: 'Nachricht ist privat!'
end
end
# Creates a new message.
def create
@message = @current_user.send_messages.new(params[:message])
if @message.save
Resque.enqueue(MessageNotifier, FoodsoftConfig.scope, 'message_deliver', @message.id)
redirect_to messages_url, :notice => I18n.t('messages.create.notice')
else
render :action => 'new'
end
end
# Shows a single message.
def show
@message = Message.find(params[:id])
unless @message.is_readable_for?(current_user)
redirect_to messages_url, alert: 'Nachricht ist privat!'
end
end
end

View file

@ -0,0 +1,21 @@
module MessagesHelper
def format_subject(message, length)
if message.subject.length > length
subject = truncate(message.subject, :length => length)
body = ""
else
subject = message.subject
body = truncate(message.body, :length => length - subject.length)
end
"<b>#{link_to(h(subject), message)}</b> <span style='color:grey'>#{h(body)}</span>".html_safe
end
def link_to_new_message(options = {})
messages_params = options[:message_params] || nil
link_text = content_tag :id, nil, class: 'icon-envelope'
link_text << " #{options[:text]}" if options[:text].present?
link_to(link_text.html_safe, new_message_path(message: messages_params), class: 'btn',
title: I18n.t('helpers.submit.message.create'))
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

@ -0,0 +1,92 @@
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => "User", :foreign_key => "sender_id"
serialize :recipients_ids, Array
attr_accessor :sent_to_all, :group_id, :recipient_tokens, :reply_to
scope :pending, -> { where(:email_state => 0) }
scope :sent, -> { where(:email_state => 1) }
scope :pub, -> { where(:private => false) }
# Values for the email_state attribute: :none, :pending, :sent, :failed
EMAIL_STATE = {
:pending => 0,
:sent => 1,
:failed => 2
}
validates_presence_of :recipients_ids, :subject, :body
validates_length_of :subject, :in => 1..255
validates_inclusion_of :email_state, :in => EMAIL_STATE.values
before_validation :clean_up_recipient_ids, :on => :create
def self.deliver(message_id)
find(message_id).deliver
end
def clean_up_recipient_ids
self.recipients_ids = recipients_ids.uniq.reject { |id| id.blank? } unless recipients_ids.nil?
self.recipients_ids = User.all.collect(&:id) if sent_to_all == "1"
end
def add_recipients(users)
self.recipients_ids = [] if recipients_ids.blank?
self.recipients_ids += users.collect(&:id) unless users.blank?
end
def group_id=(group_id)
@group_id = group_id
add_recipients Group.find(group_id).users unless group_id.blank?
end
def recipient_tokens=(ids)
@recipient_tokens = ids
add_recipients ids.split(",").collect { |id| User.find(id) }
end
def reply_to=(message_id)
@reply_to = Message.find(message_id)
add_recipients([@reply_to.sender])
self.subject = I18n.t('messages.model.reply_subject', :subject => @reply_to.subject)
self.body = I18n.t('messages.model.reply_header', :user => @reply_to.sender.display, :when => I18n.l(@reply_to.created_at, :format => :short)) + "\n"
@reply_to.body.each_line{ |l| self.body += I18n.t('messages.model.reply_indent', :line => l) }
end
def mail_to=(user_id)
user = User.find(user_id)
add_recipients([user])
end
# Returns true if this message is a system message, i.e. was sent automatically by Foodsoft itself.
def system_message?
self.sender_id.nil?
end
def sender_name
system_message? ? I18n.t('layouts.foodsoft') : sender.display rescue "?"
end
def recipients
User.find(recipients_ids)
end
def deliver
for user in recipients
if user.receive_email?
begin
MessagesMailer.foodsoft_message(self, user).deliver
rescue
Rails.logger.warn "Deliver failed for user \##{user.id}: #{user.email}"
end
end
end
update_attribute(:email_state, 1)
end
def is_readable_for?(user)
!private || sender == user || recipients_ids.include?(user.id)
end
end

View file

@ -0,0 +1,4 @@
/ insert_bottom ':root:last-child'
= config_use_heading form, :use_messages do
= config_input form, :mailing_list, as: :string, input_html: {class: 'input-xlarge'}
= config_input form, :mailing_list_subscribe, as: :string, input_html: {class: 'input-xlarge'}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,4 @@
/ insert_before 'erb:contains("simple_fields_for :notify")'
- if FoodsoftMessages.enabled?
= s.simple_fields_for :messages, defaults: { inline_label: true, label: false } do |messages|
= messages.input 'send_as_email', as: :boolean, input_html: { checked: f.object.settings.messages['send_as_email'] }

View file

@ -0,0 +1,14 @@
- if pagination
- if Message.pub.count > 20
= items_per_page
= pagination_links_remote messages
- unless messages.empty?
%table.table.table-striped
%tbody
- for message in messages
%tr
%td= format_subject(message, 130)
%td= h(message.sender_name)
%td= format_time(message.created_at)
%td= link_to t('.reply'), new_message_path(:message => {:reply_to => message.id}), class: 'btn'

View file

@ -0,0 +1,6 @@
- title t('.title')
- content_for :actionbar do
= link_to t('.new'), new_message_path, class: 'btn btn-primary'
#messages
= render 'messages', messages: @messages, pagination: true

View file

@ -0,0 +1 @@
$('#messages').html('#{j(render('messages', messages: @messages, pagination: true))}');

View file

@ -0,0 +1,46 @@
- content_for :javascript do
:javascript
$(function() {
$('#message_recipient_tokens').tokenInput("#{users_path(:format => :json)}", {
crossDomain: false,
prePopulate: $('#message_recipient_tokens').data('pre'),
hintText: '#{t '.search_user'}',
noResultText: '#{t '.no_user_found'}',
searchingText: '#{t '.search'}',
theme: 'facebook'
});
$('#message_sent_to_all').on('touchclick', function() {
if ($(this).is(':checked')) {
$('#recipients').slideUp();
} else {
$('#recipients').slideDown();
}
});
});
- title t('.title')
= simple_form_for @message do |f|
- if FoodsoftConfig[:mailing_list].blank?
= f.input :sent_to_all, :as => :boolean
- else
%b= t('.list.desc', list: mail_to(FoodsoftConfig[:mailing_list])).html_safe
%br/
%small{:style => "color:grey"}
= t '.list.subscribe_msg'
%br/
- if FoodsoftConfig[:mailing_list_subscribe].blank?
= t('.list.subscribe', link: link_to(t('.list.wiki'), wiki_page_path('MailingListe'))).html_safe
- else
= t('.list.mail', email: mail_to(FoodsoftConfig[:mailing_list_subscribe])).html_safe
#recipients
= f.input :recipient_tokens, :input_html => { 'data-pre' => User.where(id: @message.recipients_ids).map(&:token_attributes).to_json }
= f.input :group_id, :as => :select, :collection => Group.undeleted.order('type DESC', 'name ASC').reject { |g| g.memberships.empty? }
= f.input :private
= f.input :subject, input_html: {class: 'input-xxlarge'}
= f.input :body, input_html: {class: 'input-xxlarge', rows: 13}
.form-actions
= f.submit class: 'btn btn-primary'
= link_to t('ui.or_cancel'), :back

View file

@ -0,0 +1,21 @@
- title t('.title')
%div{:style => "width:40em"}
%table{:style => "width:25em"}
%tr
%td= t '.from'
%td=h @message.sender_name
%tr
%td= t '.subject'
%td
%b=h @message.subject
%tr
%td= t '.sent_on'
%td= format_time(@message.created_at)
%hr/
%p= simple_format(h(@message.body))
%hr/
%p
= link_to t('.reply'), new_message_path(:message => {:reply_to => @message.id}), class: 'btn'
|
= link_to t('.all_messages'), messages_path

View file

@ -0,0 +1,4 @@
= raw @message.body
======================================================================
\
= raw t '.footer', reply_url: new_message_url('message[reply_to]' => @message.id), msg_url: message_url(@message), profile_url: my_profile_url

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,86 @@
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
config:
hints:
mailing_list: Mailing-list email address to use instead of the messaging system for mail to all members.
mailing_list_subscribe: Email address where members can send an email to for subscribing.
use_messages: Allow members to communicate with each other within Foodsoft.
keys:
use_messages: Messages
mailing_list: Mailing-list
mailing_list_subscribe: Mailing-list subscribe
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,24 @@
# This migration has a ".skip" suffix to prevent it from being recognised as
# a migration. While this migration is relevant for the messages plugin, it
# was already included in stock foodsoft when messages were part of its core.
# `rake db:install:migrations` would install this migration, resulting in an
# attempt to create an already existing messages table.
#
# 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,11 @@
require "foodsoft_messages/engine"
require "foodsoft_messages/user_link"
require "deface"
module FoodsoftMessages
# Return whether messages are used or not.
# Enabled by default in {FoodsoftConfig} since it used to be part of the foodsoft core.
def self.enabled?
FoodsoftConfig[:use_messages]
end
end

View file

@ -0,0 +1,19 @@
module FoodsoftMessages
class Engine < ::Rails::Engine
def navigation(primary, context)
return unless FoodsoftMessages.enabled?
return if primary[:foodcoop].nil?
sub_nav = primary[:foodcoop].sub_navigation
sub_nav.items <<
SimpleNavigation::Item.new(primary, :messages, I18n.t('navigation.messages'), context.messages_path)
# move to right before tasks item
if i = sub_nav.items.index(sub_nav[:tasks])
sub_nav.items.insert(i, sub_nav.items.delete_at(-1))
end
end
def default_foodsoft_config(cfg)
cfg[:use_messages] = true
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? or not FoodsoftMessages.enabled?
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