* * @author Julius Härtl * * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ namespace OCA\Theming\Command; use OCA\Theming\ImageManager; use OCA\Theming\ThemingDefaults; use OCP\IConfig; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class UpdateConfig extends Command { public const SUPPORTED_KEYS = [ 'name', 'url', 'imprintUrl', 'privacyUrl', 'slogan', 'color' ]; public const SUPPORTED_IMAGE_KEYS = [ 'background', 'logo', 'favicon', 'logoheader' ]; private $themingDefaults; private $imageManager; private $config; public function __construct(ThemingDefaults $themingDefaults, ImageManager $imageManager, IConfig $config) { parent::__construct(); $this->themingDefaults = $themingDefaults; $this->imageManager = $imageManager; $this->config = $config; } protected function configure() { $this ->setName('theming:config') ->setDescription('Set theming app config values') ->addArgument( 'key', InputArgument::OPTIONAL, 'Key to update the theming app configuration (leave empty to get a list of all configured values)' . PHP_EOL . 'One of: ' . implode(', ', self::SUPPORTED_KEYS) ) ->addArgument( 'value', InputArgument::OPTIONAL, 'Value to set (leave empty to obtain the current value)' ) ->addOption( 'reset', 'r', InputOption::VALUE_NONE, 'Reset the given config key to default' ); } protected function execute(InputInterface $input, OutputInterface $output): int { $key = $input->getArgument('key'); $value = $input->getArgument('value'); assert(is_string($value) || $value === null, 'At most one value should be provided.'); if ($key === null) { $output->writeln('Current theming config:'); foreach (self::SUPPORTED_KEYS as $key) { $value = $this->config->getAppValue('theming', $key, ''); $output->writeln('- ' . $key . ': ' . $value . ''); } foreach (self::SUPPORTED_IMAGE_KEYS as $key) { $value = $this->config->getAppValue('theming', $key . 'Mime', ''); $output->writeln('- ' . $key . ': ' . $value . ''); } return 0; } if (!in_array($key, self::SUPPORTED_KEYS, true) && !in_array($key, self::SUPPORTED_IMAGE_KEYS, true)) { $output->writeln('Invalid config key provided'); return 1; } if ($input->getOption('reset')) { $defaultValue = $this->themingDefaults->undo($key); $output->writeln('Reset ' . $key . ' to ' . $defaultValue . ''); return 0; } if ($value === null) { $value = $this->config->getAppValue('theming', $key, ''); if ($value !== '') { $output->writeln('' . $key . ' is currently set to ' . $value . ''); } else { $output->writeln('' . $key . ' is currently not set'); } return 0; } if (in_array($key, self::SUPPORTED_IMAGE_KEYS, true)) { if (strpos($value, '/') !== 0) { $output->writeln('The image file needs to be provided as an absolute path: ' . $value . '.'); return 1; } if (!file_exists($value)) { $output->writeln('File could not be found: ' . $value . '.'); return 1; } $value = $this->imageManager->updateImage($key, $value); $key = $key . 'Mime'; } if ($key === 'color' && !preg_match('/^\#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) { $output->writeln('The given color is invalid: ' . $value . ''); return 1; } $this->themingDefaults->set($key, $value); $output->writeln('Updated ' . $key . ' to ' . $value . ''); return 0; } } ' href='#n24'>24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
# redMine - project management software
# Copyright (C) 2006-2007  Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

class Mailer < ActionMailer::Base
  helper :application
  helper :issues
  helper :custom_fields
  
  include ActionController::UrlWriter
  
  def issue_add(issue)    
    redmine_headers 'Project' => issue.project.identifier,
                    'Issue-Id' => issue.id,
                    'Issue-Author' => issue.author.login
    redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
    recipients issue.recipients    
    subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
    body :issue => issue,
         :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
  end

  def issue_edit(journal)
    issue = journal.journalized
    redmine_headers 'Project' => issue.project.identifier,
                    'Issue-Id' => issue.id,
                    'Issue-Author' => issue.author.login
    redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
    recipients issue.recipients
    # Watchers in cc
    cc(issue.watcher_recipients - @recipients)
    s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
    s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
    s << issue.subject
    subject s
    body :issue => issue,
         :journal => journal,
         :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
  end
  
  def document_added(document)
    redmine_headers 'Project' => document.project.identifier
    recipients document.project.recipients
    subject "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
    body :document => document,
         :document_url => url_for(:controller => 'documents', :action => 'show', :id => document)
  end
  
  def attachments_added(attachments)
    container = attachments.first.container
    added_to = ''
    added_to_url = ''
    case container.class.name
    when 'Version'
      added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container.project_id)
      added_to = "#{l(:label_version)}: #{container.name}"
    when 'Document'
      added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
      added_to = "#{l(:label_document)}: #{container.title}"
    end
    redmine_headers 'Project' => container.project.identifier
    recipients container.project.recipients
    subject "[#{container.project.name}] #{l(:label_attachment_new)}"
    body :attachments => attachments,
         :added_to => added_to,
         :added_to_url => added_to_url
  end

  def news_added(news)
    redmine_headers 'Project' => news.project.identifier
    recipients news.project.recipients
    subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
    body :news => news,
         :news_url => url_for(:controller => 'news', :action => 'show', :id => news)
  end

  def message_posted(message, recipients)
    redmine_headers 'Project' => message.project.identifier,
                    'Topic-Id' => (message.parent_id || message.id)
    recipients(recipients)
    subject "[#{message.board.project.name} - #{message.board.name}] #{message.subject}"
    body :message => message,
         :message_url => url_for(:controller => 'messages', :action => 'show', :board_id => message.board_id, :id => message.root)
  end
   
  def account_information(user, password)
    set_language_if_valid user.language
    recipients user.mail
    subject l(:mail_subject_register, Setting.app_title)
    body :user => user,
         :password => password,
         :login_url => url_for(:controller => 'account', :action => 'login')
  end
  
  def account_activation_request(user)
    # Send the email to all active administrators
    recipients User.find_active(:all, :conditions => {:admin => true}).collect { |u| u.mail }.compact
    subject l(:mail_subject_account_activation_request, Setting.app_title)
    body :user => user,
         :url => url_for(:controller => 'users', :action => 'index', :status => User::STATUS_REGISTERED, :sort_key => 'created_on', :sort_order => 'desc')
  end

  def lost_password(token)
    set_language_if_valid(token.user.language)
    recipients token.user.mail
    subject l(:mail_subject_lost_password, Setting.app_title)
    body :token => token,
         :url => url_for(:controller => 'account', :action => 'lost_password', :token => token.value)
  end  

  def register(token)
    set_language_if_valid(token.user.language)
    recipients token.user.mail
    subject l(:mail_subject_register, Setting.app_title)
    body :token => token,
         :url => url_for(:controller => 'account', :action => 'activate', :token => token.value)
  end
  
  def test(user)
    set_language_if_valid(user.language)
    recipients user.mail
    subject 'Redmine test'
    body :url => url_for(:controller => 'welcome')
  end

  # Overrides default deliver! method to prevent from sending an email
  # with no recipient, cc or bcc
  def deliver!(mail = @mail)
    return false if (recipients.nil? || recipients.empty?) && 
                    (cc.nil? || cc.empty?) &&
                    (bcc.nil? || bcc.empty?)
    super
  end

  private
  def initialize_defaults(method_name)
    super
    set_language_if_valid Setting.default_language
    from Setting.mail_from
    default_url_options[:host] = Setting.host_name
    default_url_options[:protocol] = Setting.protocol
    # Common headers
    headers 'X-Mailer' => 'Redmine',
            'X-Redmine-Host' => Setting.host_name,
            'X-Redmine-Site' => Setting.app_title
  end
  
  # Appends a Redmine header field (name is prepended with 'X-Redmine-')
  def redmine_headers(h)
    h.each { |k,v| headers["X-Redmine-#{k}"] = v }
  end
  
  # Overrides the create_mail method
  def create_mail
    # Removes the current user from the recipients and cc
    # if he doesn't want to receive notifications about what he does
    if User.current.pref[:no_self_notified]
      recipients.delete(User.current.mail) if recipients
      cc.delete(User.current.mail) if cc
    end
    # Blind carbon copy recipients
    if Setting.bcc_recipients?
      bcc([recipients, cc].flatten.compact.uniq)
      recipients []
      cc []
    end    
    super
  end
  
  # Renders a message with the corresponding layout
  def render_message(method_name, body)
    layout = method_name.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml'
    body[:content_for_layout] = render(:file => method_name, :body => body)
    ActionView::Base.new(template_root, body, self).render(:file => "mailer/#{layout}")
  end
  
  # Makes partial rendering work with Rails 1.2 (retro-compatibility)
  def self.controller_path
    ''
  end unless respond_to?('controller_path')
end