From 009abd764ab72ad5fc67f6bd02b5f600cebf4e52 Mon Sep 17 00:00:00 2001
From: Gabriel Pettier <gabriel.pettier@alterway.fr>
Date: Wed, 25 Jul 2012 18:51:23 +0200
Subject: [PATCH] add private messages

apply manually private_messages-2011-12-14-v1.3.patch
and add 20110820092133_add_private_to_journals.rb

from http://www.redmine.org/issues/1554
---
 app/controllers/activities_controller.rb           |    1 +
 app/controllers/issues_controller.rb               |    3 +-
 app/controllers/journals_controller.rb             |    2 +-
 app/helpers/journals_helper.rb                     |    8 +++++
 app/models/issue.rb                                |    4 +-
 app/models/mail_handler.rb                         |    1 +
 app/models/mailer.rb                               |   31 ++++++++++++++++++-
 app/views/issues/_edit.html.erb                    |    8 +++++
 app/views/issues/_history.html.erb                 |   25 +++++++++++++--
 app/views/issues/show.html.erb                     |   11 ++++--
 app/views/journals/_notes_form.html.erb            |    3 ++
 .../20110820092133_add_private_to_journals.rb      |    9 ++++++
 lib/redmine.rb                                     |    2 +
 13 files changed, 94 insertions(+), 14 deletions(-)
 create mode 100644 db/migrate/20110820092133_add_private_to_journals.rb

diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb
index bdf0b24..5c24ba6 100644
--- a/app/controllers/activities_controller.rb
+++ b/app/controllers/activities_controller.rb
@@ -38,6 +38,7 @@ class ActivitiesController < ApplicationController
     @activity.scope_select {|t| !params["show_#{t}"].nil?}
     @activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
 
+    events.delete_if { |e| e.is_a?(Journal) && e.private && !User.current.allowed_to?(:view_private_messages, @project) }
     events = @activity.events(@date_from, @date_to)
 
     if events.empty? || stale?(:etag => [@activity.scope, @date_to, @date_from, @with_subprojects, @author, events.first, User.current, current_language])
diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index cffb69b..adcc584 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -360,7 +360,8 @@ private
     @time_entry.attributes = params[:time_entry]
 
     @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
-    @issue.init_journal(User.current, @notes)
+    @private_message = params[:private_message] || false
+    @issue.init_journal(User.current, @notes, @private_message)
 
     issue_attributes = params[:issue]
     if issue_attributes && params[:conflict_resolution]
diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb
index faf4b38..50786a7 100644
--- a/app/controllers/journals_controller.rb
+++ b/app/controllers/journals_controller.rb
@@ -82,7 +82,7 @@ class JournalsController < ApplicationController
   def edit
     (render_403; return false) unless @journal.editable_by?(User.current)
     if request.post?
-      @journal.update_attributes(:notes => params[:notes]) if params[:notes]
+      @journal.update_attributes(:private => params[:private_message] || false)
       @journal.destroy if @journal.details.empty? && @journal.notes.blank?
       call_hook(:controller_journals_edit_post, { :journal => @journal, :params => params})
       respond_to do |format|
diff --git a/app/helpers/journals_helper.rb b/app/helpers/journals_helper.rb
index 958bde9..f7c2591 100644
--- a/app/helpers/journals_helper.rb
+++ b/app/helpers/journals_helper.rb
@@ -41,4 +41,12 @@ module JournalsHelper
     onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
     link_to text, '#', options.merge(:onclick => onclick)
   end
+
+  def only_private_messages?(journals)
+     journals.each do |journal|
+         return false if ! journal.private
+     end
+
+     true
+  end
 end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 3e1985a..7382f1b 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -444,8 +444,8 @@ class Issue < ActiveRecord::Base
     end
   end
 
-  def init_journal(user, notes = "")
-    @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes)
+  def init_journal(user, notes = "", private = false)
+    @current_journal ||= Journal.new(:journalized => self, :user => user, :notes => notes, :private => private)
     if new_record?
       @current_journal.notify = false
     else
diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb
index e920dbd..528bedd 100644
--- a/app/models/mail_handler.rb
+++ b/app/models/mail_handler.rb
@@ -190,6 +190,7 @@ class MailHandler < ActionMailer::Base
     issue.safe_attributes = issue_attributes_from_keywords(issue)
     issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
     journal.notes = cleaned_up_text_body
+    journal.private = 1 if !email.subject.to_s.index('[PRIVATE]').nil?
     add_attachments(issue)
     issue.save!
     if logger && logger.info
diff --git a/app/models/mailer.rb b/app/models/mailer.rb
index d18394c..df90f71 100644
--- a/app/models/mailer.rb
+++ b/app/models/mailer.rb
@@ -57,6 +57,7 @@ class Mailer < ActionMailer::Base
   #   Mailer.deliver_issue_edit(journal) => sends an email to issue recipients
   def issue_edit(journal)
     issue = journal.journalized.reload
+    private = journal[:private]
     redmine_headers 'Project' => issue.project.identifier,
                     'Issue-Id' => issue.id,
                     'Issue-Author' => issue.author.login
@@ -64,11 +65,18 @@ class Mailer < ActionMailer::Base
     message_id journal
     references issue
     @author = journal.user
-    recipients issue.recipients
+    @recipients ||= []
+    watchers = issue.watcher_recipients - @recipients
+
+    processed_recipients = prepare_receivers issue.recipients, issue.project, private
+    processed_watchers = prepare_receivers watchers, issue.project, private
+
+    recipients processed_recipients
     # Watchers in cc
-    cc(issue.watcher_recipients - @recipients)
+    cc(processed_watchers)
     s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
     s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
+    s << "[PRIVATE] " if journal.private
     s << issue.subject
     subject s
     body :issue => issue,
@@ -487,6 +495,25 @@ class Mailer < ActionMailer::Base
   def mylogger
     Rails.logger
   end
+
+  # Method for recipients or watchers preparation
+  def prepare_receivers(receivers, project, private)
+      processed_receivers = Array.new()
+
+      receivers.each do |recepient|
+         current_user = User.find_by_mail(recepient)
+         can_see_private_messages = current_user.allowed_to?(:view_private_messages, project)
+
+         if private
+             next if ! can_see_private_messages
+         end
+
+         processed_receivers << recepient
+      end
+
+      processed_receivers
+   end
+
 end
 
 # Patch TMail so that message_id is not overwritten
diff --git a/app/views/issues/_edit.html.erb b/app/views/issues/_edit.html.erb
index 690fa93..ce43366 100644
--- a/app/views/issues/_edit.html.erb
+++ b/app/views/issues/_edit.html.erb
@@ -1,5 +1,8 @@
 <% labelled_form_for @issue, :html => {:id => 'issue-form', :multipart => true} do |f| %>
     <%= error_messages_for 'issue', 'time_entry' %>
+
+    <% view_private = User.current.allowed_to?(:view_private_messages, @project) %>
+
     <%= render :partial => 'conflict' if @conflict %>
     <div class="box">
     <% if @edit_allowed || !@allowed_statuses.empty? %>
@@ -31,6 +34,11 @@
     <%= wikitoolbar_for 'notes' %>
     <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
 
+    <% if view_private %>
+      <%= check_box_tag :private_message, true, false %>
+      <%= label_tag :private_message, 'Private message', :style => 'font-weight:bold;' %>
+    <% end %>
+
     <p><%=l(:label_attachment_plural)%><br /><%= render :partial => 'attachments/form', :locals => {:container => @issue} %></p>
     </fieldset>
     </div>
diff --git a/app/views/issues/_history.html.erb b/app/views/issues/_history.html.erb
index aea0d1e..704e560 100644
--- a/app/views/issues/_history.html.erb
+++ b/app/views/issues/_history.html.erb
@@ -1,10 +1,27 @@
 <% reply_links = authorize_for('issues', 'edit') -%>
+<% view_private = User.current.allowed_to?(:view_private_messages, @project) %>
+<% @skipped = 0 %>
+
 <% for journal in journals %>
-  <div id="change-<%= journal.id %>" class="<%= journal.css_classes %>">
-    <h4><%= link_to "##{journal.indice}", {:anchor => "note-#{journal.indice}"}, :class => "journal-link" %>
+
+<% private = journal.private %>
+
+<% if private %>
+  <% if ! view_private %>
+    <% @skipped += 1 %>
+    <% next %>
+  <% end %>
+<% end %>
+
+  <div id="change-<%= journal.id %>" class="<%= journal.css_classes %><%= ' private' if journal.private %>">
+    <h4><div class="journal-link"><%= link_to "##{journal.indice - @skipped}", :anchor => "note-#{journal.indice - @skipped}" %></div>
     <%= avatar(journal.user, :size => "24") %>
-    <%= content_tag('a', '', :name => "note-#{journal.indice}")%>
-    <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %></h4>
+    <%= content_tag('a', '', :name => "note-#{journal.indice - @skipped}")%>
+    <%= authoring journal.created_on, journal.user, :label => :label_updated_time_by %>
+    <% if journal.private %>
+      <span>[PRIVATE]</span>
+    <% end %>
+    </h4>
 
     <% if journal.details.any? %>
     <ul class="details">
diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb
index b33fe49..016243b 100644
--- a/app/views/issues/show.html.erb
+++ b/app/views/issues/show.html.erb
@@ -112,10 +112,13 @@
 <% end %>
 
 <% if @journals.present? %>
-<div id="history">
-<h3><%=l(:label_history)%></h3>
-<%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
-</div>
+  <% view_private = User.current.allowed_to?(:view_private_messages, @project) %>
+  <% if ! only_private_messages?(@journals) || view_private %>
+    <div id="history">
+    <h3><%=l(:label_history)%></h3>
+    <%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
+    </div>
+  <% end %>
 <% end %>
 
 
diff --git a/app/views/journals/_notes_form.html.erb b/app/views/journals/_notes_form.html.erb
index f1311ac..8d519cd 100644
--- a/app/views/journals/_notes_form.html.erb
+++ b/app/views/journals/_notes_form.html.erb
@@ -17,6 +17,9 @@
     <%= link_to l(:button_cancel), '#', :onclick => "Element.remove('journal-#{@journal.id}-form'); " +
                                                     "Element.show('journal-#{@journal.id}-notes'); return false;" %></p>
 
+    <%= check_box_tag :private_message, true, @journal.private, :id => "private_message_#{@journal.id}" %>
+    <%= label_tag "private_message_#{@journal.id}", 'Private message', :style => 'font-weight:bold;' %>
+
     <div id="journal_<%= @journal.id %>_preview" class="wiki"></div>
 <% end %>
 <%= wikitoolbar_for "journal_#{@journal.id}_notes" %>
diff --git a/db/migrate/20110820092133_add_private_to_journals.rb b/db/migrate/20110820092133_add_private_to_journals.rb
new file mode 100644
index 0000000..d21911d
--- /dev/null
+++ b/db/migrate/20110820092133_add_private_to_journals.rb
@@ -0,0 +1,9 @@
+class AddPrivateToJournals < ActiveRecord::Migration
+  def self.up
+     add_column :journals, :private, :boolean, :default => false
+  end
+
+  def self.down
+     remove_column :journals, :private
+  end
+end
diff --git a/lib/redmine.rb b/lib/redmine.rb
index 156bc76..1ee5c69 100644
--- a/lib/redmine.rb
+++ b/lib/redmine.rb
@@ -85,6 +85,8 @@ Redmine::AccessControl.map do |map|
     map.permission :view_issue_watchers, {}
     map.permission :add_issue_watchers, {:watchers => :new}
     map.permission :delete_issue_watchers, {:watchers => :destroy}
+    # Private messages
+    map.permission :view_private_messages, {}
   end
 
   map.project_module :time_tracking do |map|
-- 
1.7.2.5

