Repository: jfqd/redmine_helpdesk Branch: master Commit: 08468822690e Files: 94 Total size: 130.0 KB Directory structure: gitextract_lz5v3in0/ ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── app/ │ └── views/ │ ├── _issue_edit.erb │ └── _issue_history.erb ├── config/ │ └── locales/ │ ├── de.yml │ ├── en.yml │ ├── es.yml │ ├── it.yml │ ├── ja.yml │ ├── pl.yml │ ├── ru.yml │ ├── tr.yml │ └── zh.yml ├── db/ │ └── migrate/ │ ├── 001_create_custom_owner_email_field.rb │ ├── 002_create_custom_fields_for_reply.rb │ ├── 003_create_custom_field_for_sender_email.rb │ ├── 004_create_custom_field_for_send_to_owner_default.rb │ ├── 005_add_treat_as_supportclient_to_anonymous.rb │ ├── 006_append_footer_to_first_reply.rb │ ├── 007_create_custom_copy_to_field.rb │ ├── 008_create_custom_field_cc_handling.rb │ ├── 009_create_custom_field_for_reopen_closed_issues_by_email.rb │ └── 010_create_custom_field_for_reply_separator.rb ├── init.rb ├── lib/ │ ├── helpdesk_hooks.rb │ ├── helpdesk_mailer.rb │ ├── macro_expander.rb │ ├── redmine_helpdesk/ │ │ ├── journal_patch.rb │ │ ├── mail_handler_patch.rb │ │ └── mailer_patch.rb │ └── tasks/ │ ├── local-db.rake │ ├── plugin_ci.rake │ └── redmine.rake └── test/ ├── confs/ │ ├── database_mysql.yml │ ├── database_postgres.yml │ ├── database_postgres_ext.yml │ └── database_sqlite.yml ├── fixtures/ │ ├── 2.6/ │ │ ├── attachments.yml │ │ ├── custom_fields.yml │ │ ├── custom_fields_projects.yml │ │ ├── custom_fields_trackers.yml │ │ ├── custom_values.yml │ │ ├── enabled_modules.yml │ │ ├── enumerations.yml │ │ ├── issue_statuses.yml │ │ ├── issues.yml │ │ ├── journal_details.yml │ │ ├── journals.yml │ │ ├── member_roles.yml │ │ ├── members.yml │ │ ├── projects.yml │ │ ├── projects_trackers.yml │ │ ├── roles.yml │ │ ├── trackers.yml │ │ └── users.yml │ ├── 3.0/ │ │ ├── attachments.yml │ │ ├── custom_fields.yml │ │ ├── custom_fields_projects.yml │ │ ├── custom_fields_trackers.yml │ │ ├── custom_values.yml │ │ ├── email_addresses.yml │ │ ├── enabled_modules.yml │ │ ├── enumerations.yml │ │ ├── issue_statuses.yml │ │ ├── issues.yml │ │ ├── journal_details.yml │ │ ├── journals.yml │ │ ├── member_roles.yml │ │ ├── members.yml │ │ ├── projects.yml │ │ ├── projects_trackers.yml │ │ ├── roles.yml │ │ ├── trackers.yml │ │ └── users.yml │ ├── files/ │ │ └── 2006/ │ │ └── 07/ │ │ └── 060719210727_source.rb │ └── mail_handler/ │ ├── ticket_by_unknown_user.eml │ ├── ticket_by_unknown_user_with_cc.eml │ ├── ticket_by_user_1.eml │ ├── ticket_by_user_2.eml │ ├── ticket_reply.eml │ ├── ticket_with_attachment.eml │ └── ticket_with_attributes.eml ├── functional/ │ └── issues_controller_with_helpdesk_test.rb ├── test_helper.rb └── unit/ ├── helpdesk_mailer_test.rb ├── journal_patch_test.rb ├── macro_expander_test.rb └── mail_handler_patch_test.rb ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store *~ test/app/* ================================================ FILE: .travis.yml ================================================ language: ruby rvm: - 2.5 - 2.6 - jruby services: - mysql - postgresql env: global: - REDMINE_LANG=en - MYSQL_DATABASE=redmine - MYSQL_HOST=127.0.0.1 - MYSQL_PORT=3306 - MYSQL_USER=root - MYSQL_PASSWORD= - POSTGRES_DATABASE=redmine - POSTGRES_USER=postgres matrix: - REDMINE_VERSION=4.0.5 DATABASE_ADAPTER=mysql - REDMINE_VERSION=4.0.5 DATABASE_ADAPTER=postgresql matrix: allow_failures: # Incomplete ActiveRecord 4.2 support in jruby - rvm: 2.6 env: REDMINE_VERSION=4.0.5 DATABASE_ADAPTER=mysql - rvm: 2.6 env: REDMINE_VERSION=4.0.5 DATABASE_ADAPTER=postgresql - rvm: jruby env: REDMINE_VERSION=4.0.5 DATABASE_ADAPTER=mysql - rvm: jruby env: REDMINE_VERSION=4.0.5 DATABASE_ADAPTER=postgresql addons: code_climate: repo_token: secure: "SAyz/KNRQQC1T2oGNUxbDceBcoaL/IxvEmeKEIR3iivj3RMJi2Nm8v5B2Su9MhQBegi2mH70ypgkTPmGh69tSONf1sqkkCAxsZnIqihl1Ai+dwM2KlBLGw/k+IG9/xD1+hgQiO06OKZMPO+Kj9X28mMqJ75YVKGE4alr1mfo2NM=" before_install: - rake helpdesk:redmine:install - cd test/app - export BUNDLE_GEMFILE=Gemfile before_script: - echo $(pwd) - sudo service mysql start - echo $(ps -Af | grep mysqld) - export RAILS_ENV=test - mysql -e 'create database redmine;' - psql -c 'create database redmine;' -U postgres - bundle exec rake generate_secret_token - export RAILS_ENV=test - bundle exec rake db:migrate script: - bundle exec rake helpdesk:ci ================================================ FILE: CHANGELOG.md ================================================ 0.0.20 --- * Make plugin compatibility with Redmine 5.0.x 0.0.19 --- * Italian translation * Fix logger and use new setting * Do not send on private-notes * Fix reopening closed issue error * Improve de locale * Fix typo in email_was_send_to_supportclient * Sets To header optional * Remove Gemfile.lock * Find and process value of custom field directly * Add X-Redmine-Issue-Tracker header to mailerpatch 0.0.18 --- * Remove batches as nobody has the time to fix the tests * Remove untrue stuff from readme * Update mail_handler_patch.rb * Fix migration * Incorrect issue and project ids usage * Add space after the pre 0.0.17 --- * Fix issue by sending redmine journals with redmine_helpdesk * Store email-details before each note by martincizek from orchitech * Make option to reopen closed issues by email work * Added support for reply separator by sandratatarevicova from orchitech 0.0.16 --- * Make plugin compatibility with Redmine 4.0.x * Add option to reopen closed issues by email 0.0.15 --- * Added support for tracking email details by martincizek from orchitech 0.0.14 --- * Make plugin compatibility with Redmine 3.0.1 by Vilppu Vuorinen 0.0.13 --- * Unit and functional tests with travis and code climate support by Vilppu Vuorinen * Add customizable email footers by martincizek * Test compatibility with Redmine 2.6.2 0.0.12 --- * Add support for non-anonymous supportclients by martincizek * Add issue matching based on standard MIME header references by martincizek * Test compatibility with Redmine 2.5.3 0.0.11 --- * Make sure that the notes length is always calculated 0.0.10 --- * Fixed bug trying to send an email with empty notes 0.0.9 --- * Fixed non-working helpdesk-send-to-owner-default checkbox 0.0.8 --- * Add setting for handling send to owner default value by davidef 0.0.7 --- * Added reply-to header by barbazul 0.0.6 --- * Update code for redmine 2.4.x by Craig Gowing * Minor compatibility issues fixed 0.0.5 --- * Fix skip validation issue 0.0.4 --- * Update code for redmine 2.3.x * Send any journal attachments with the email notification to the supportclient 0.0.3 --- * The sender email-address is now adjustable on a per project basis 0.0.2 --- * A standard first reply message can be send to the supportclient on ticket creation (optional, per project) * The email-footer for the email notification to the supportclient can be adjusted (optional, per project) 0.0.1 --- * First release ================================================ FILE: CONTRIBUTING.md ================================================ # Issues Please note that I cannot provide user support for this open source project. All issues related to user support will be closed without a further comment. Open source does not mean that I have unlimited time to solve problems of others for free! If you need professional help please [contact me](http://www.qutic.com/support "hire a consultant") directly - I am for hire. If you find a bug please create an issue in the tracker, write a patch and send me a pull request :-) ================================================ FILE: Gemfile ================================================ gem "codeclimate-test-reporter", group: :test, require: nil unless (Gem::Specification::find_all_by_name('rake').any?) gem "rake", group: :test, require: nil end ================================================ FILE: LICENSE ================================================ Copyright (c) 2012-2021 qutic development GmbH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Redmine Helpdesk Lightweight helpdesk plugin for redmine. Adds the email sender-address of an anonymous supportclient to the custom field 'owner-email' of a ticket which was created by a support email. Answers can be send to the supportclient by checking the support checkbox on a journal. ## Support You want to support the development of this plugin? [We provide Redmine hosting including the Helpdesk plugin](https://qutic.com/de/loesungen/redmine-hosting/). ## Features * No need to create any user accounts for anonymous user * Support for sending an email notification to the (anonymous user) supportclient on ticket creation * A standard first reply message can be send to the supportclient on ticket creation (optional, per project) * The email-footer for the email notification to the supportclient can be adjusted (optional, per project) * The email-footer can be customized by using the following placeholders: - Issue parameters - ##issue-id## - ##issue-subject## - ##issue-tracker## - ##issue-status## - Project parameters - ##project-name## - User parameters - ##user-name## - ##user-firstname## - ##user-lastname## - ##user-mail## - ##user-login## - all user custom fields: ##user-cf-[user's custom field name]##, e.g. ##user-cf-position## - Base parameters - ##time-now## * The sender email-address can be adjusted (optional, per project) * Internal communication is not send to the supportclient * The supportclient will get an email notification if the support checkbox on the journal is checked (default value is optional) * Journal attachments will be delivered too * Cc header is handled if the cc-handling checkbox is checked. (optional, per project) * Reopening a closed issue is handled if the reopen-closed-issues-by-email checkbox is checked. (optional, per project) ## Screenshot ![Send mail to supportclient](doc/send-mail-to-supportclient.jpg "New checkbox 'Send mail to supportclient'") ## Getting the plugin A copy of the plugin can be downloaded from [GitHub](https://github.com/jfqd/redmine_helpdesk) ## Installation To install the plugin clone the repo from github and migrate the database: ``` cd /path/to/redmine/ git clone git://github.com/jfqd/redmine_helpdesk.git plugins/redmine_helpdesk bundle install --without development test rmagick bundle exec rake redmine:plugins:migrate RAILS_ENV=production ``` To uninstall the plugin migrate the database back and remove the plugin: ``` cd /path/to/redmine/ rake redmine:plugins:migrate NAME=redmine_helpdesk VERSION=0 RAILS_ENV=production rm -rf plugins/redmine_helpdesk ``` Further information about plugin installation can be found at: https://www.redmine.org/wiki/redmine/Plugins ## Usage To use the helpdesk functionality you need to * add the custom field 'owner-email' to a project in the project configuration * add a standard first reply message into the custom_field 'helpdesk-first-reply' in the project configuration (optional) * add an email-footer into the custom_field 'helpdesk-email-footer' in the project configuration * add a sender email address into the custom_field 'helpdesk-sender-email' in the project configuration (optional) * make sure 'Issue added' and 'Issue updated' in the general redmine settings for email notifications are checked * add the permission 'Treat as supportclient' to all roles you want to be treated as supportclient (the permission is automatically added to the 'Anonymous' role) * disable standard notifications for non-anonymous supportclients to prevent their spamming (optional) * add a cronjob for creating issues from support emails ![project configuration sample](doc/project-settings.jpg "Per project configuration sample") ## Cronjob Creating tickets from support emails through an IMAP-account is done by a cronjob. If you are not familiar with cron you first should read about the concept. The following syntax is for ubuntu or debian linux: ``` */5 * * * * redmineuser /path/to/your/rake -f /path/to/redmine/Rakefile --silent redmine:email:receive_imap RAILS_ENV="production" host=mail.example.com port=993 username=username password=password ssl=true project=project_identifier folder=INBOX move_on_success=processed move_on_failure=failed no_permission_check=1 unknown_user=accept 1 > /dev/null ``` Further information about receiving emails with redmine can be found at: [https://www.redmine.org/projects/redmine/wiki/RedmineReceivingEmails](https://www.redmine.org/projects/redmine/wiki/RedmineReceivingEmails#Fetching-emails-from-an-IMAP-server) Please note that forwarding emails with **rdm-mailhandler.rb** is currently **not supported** by the plugin. ## Compatibility The latest version of this plugin is only compatible with Redmine 5.0, 5.1. * A version for Redmine 4.2.x is tagged with [v4.2](https://github.com/jfqd/redmine_helpdesk/releases/tag/v4.2 "plugin version for Redmine 4.2.x") and available for [download on github](https://github.com/jfqd/redmine_helpdesk/archive/v4.2.zip "download plugin for Redmine 4.2.x"). * A version for Redmine 4.0.x is tagged with [v4.0](https://github.com/jfqd/redmine_helpdesk/releases/tag/v4.0 "plugin version for Redmine 4.0.x") and available for [download on github](https://github.com/jfqd/redmine_helpdesk/archive/v4.0.zip "download plugin for Redmine 4.0.x"). * A version for Redmine 3.0.x and 3.1.x is tagged with [v3.1](https://github.com/jfqd/redmine_helpdesk/releases/tag/v3.1 "plugin version for Redmine 3.0.x and 3.1.x") and available for [download on github](https://github.com/jfqd/redmine_helpdesk/archive/v3.1.zip "download plugin for Redmine 3.0.x and 3.1.x"). * A version for Redmine 2.4.x and 2.5.x is tagged with [v2.5](https://github.com/jfqd/redmine_helpdesk/releases/tag/v2.5 "plugin version for Redmine 2.4.x and 2.5.x") and available for [download on github](https://github.com/jfqd/redmine_helpdesk/archive/v2.5.zip "download plugin for Redmine 2.4.x and 2.5.x"). * A version for Redmine 2.3.x is tagged with [v2.3](https://github.com/jfqd/redmine_helpdesk/tree/v2.3 "plugin version for Redmine 2.3.x") and available for [download on github](https://github.com/jfqd/redmine_helpdesk/archive/v2.3.zip "download plugin for Redmine 2.3.x"). * A version for Redmine 1.2.x. up to 1.4.7. is tagged with [v1.4](https://github.com/jfqd/redmine_helpdesk/tree/v1.4 "plugin version for Redmine 1.2.x up to 1.4.7") and available for [download on github](https://github.com/jfqd/redmine_helpdesk/archive/v1.4.zip "download plugin for Redmine 1.2.x up to 1.4.7"). If you prefer to run Redmine with JRuby make sure to use Redmine versions prior to 3.0.x! ## Development ### Testing Rake tasks for testing against Redmine are provided. Redmine is installed under `test/app` and tests are run against the local instance. ```bash REDMINE_VERSION=3.2 DATABASE_ADAPTER=mysql rake helpdesk:redmine:install ``` Docker is used to provide local MySQL (`DATABASE_ADAPTER=mysql`) and PostgreSQL (`DATABASE_ADAPTER=postgresql_ext`) instances. ```bash export DATABASE_ADAPTER=mysql rake helpdesk:prepare_local rake helpdesk:migrate ``` Test suite can be executed with: ```bash DATABASE_ADAPTER=mysql rake helpdesk:ext_ci ``` The local database instance has to be stopped with a rake task: ```bash rake helpdesk:localdb:stop ``` ## Contributions * [MatthiasPetermann](https://github.com/MatthiasPetermann) - Update for compatibility with Redmine 5.x / Rails 6.x * [rrsach](https://github.com/rrsach) - Redmine 5 Compatibility * [gianpaol0](https://github.com/gianpaol0) - italian translation * [mgeerdsen](https://github.com/mgeerdsen) - Fix reopening closed issue error * [mgeerdsen](https://github.com/mgeerdsen) - Improve de locale * [lmorillas](https://github.com/lmorillas) - Fix Error when message hasn't "To:" header * [promasu](https://github.com/promasu) - Add X-Redmine-Issue-Tracker header to mailerpatch * [ANemcov](https://github.com/ANemcov) - incorrect issue and project ids usage * [ismaelgc](https://github.com/ismaelgc) - Spanish translation * [monaka](https://github.com/monaka) - Japanese translation * [avoidik](https://github.com/avoidik) - Add missed Russian translation into roles and access manager * [WhereIsPedro](https://github.com/WhereIsPedro) - Polish translation * [vilppuvuorinen](https://github.com/vilppuvuorinen) - 3.0.x compatibility * [vilppuvuorinen](https://github.com/vilppuvuorinen) - Unit and functional tests with travis and code climate support * [ssidelnikov](https://github.com/ssidelnikov) - Make sure that the notes length is always calculated * [nifuki](https://github.com/nifuki) - Fixed bug trying to send an email with empty notes * [nifuki](https://github.com/nifuki) - Fixed non-working helpdesk-send-to-owner-default checkbox * [box789](https://github.com/box789) - Russian translation * [seqizz](https://github.com/seqizz) - Turkish translation * [benstwa](https://github.com/benstwa) - 'send' should be 'sent' * [davidef](https://github.com/davidef) - Add setting for handling sent to owner default value * [Craig Gowing](https://github.com/craiggowing) - Redmine 2.4 compatibility * [Barbazul](https://github.com/barbazul) - Added reply-to header * [Orchitech Solutions](https://github.com/orchitech) - Added issue matching based on standard MIME header references * [Orchitech Solutions](https://github.com/orchitech) - Added support for non-anonymous supportclients (sponsored by ISIC Global Office) * [Orchitech Solutions](https://github.com/orchitech) - Added support for customizable email footers (sponsored by ISIC Global Office) * [Orchitech Solutions](https://github.com/orchitech) - Added support for tracking email details (sponsored by ISIC Global Office) * [shackijj](https://github.com/shackijj) - Added Cc header handling * [archonwang](https://github.com/archonwang) - Add Simplified Chinese * [Niremizov](https://github.com/Niremizov) - Set owner email only if it wasn't set before * [ghost](https://github.com/ghost) - Fix attachements truncated in email sent to supportclient * [Orchitech Solutions](https://github.com/orchitech) - Added support for reply separator (sponsored by ISIC Global Office) ## License This plugin is licensed under the MIT license. See LICENSE-file for details. ## Copyright Copyright (c) 2012-2022 qutic development GmbH ================================================ FILE: Rakefile ================================================ Dir.glob('lib/tasks/*.rake').each { |r| load r} ================================================ FILE: app/views/_issue_edit.erb ================================================ <%= check_box_tag('send_to_owner', "true", (send_to_owner_default == "1")) %> <%=h t('label_support_checkbox') %> (<%=h email %>) ================================================ FILE: app/views/_issue_history.erb ================================================ <%=h t('email_was_send_to_supportclient') %> ================================================ FILE: config/locales/de.yml ================================================ de: label_support_checkbox: "E-Mail an Support-Anfragenden senden" email_was_send_to_supportclient: "Diese Antwort wurde an den Support-Anfragenden gesendet." permission_treat_user_as_supportclient: "Nutzer als Support-Anfragenden behandeln" ================================================ FILE: config/locales/en.yml ================================================ en: label_support_checkbox: "Send mail to supportclient" email_was_send_to_supportclient: "This answer was sent to the supportclient." ================================================ FILE: config/locales/es.yml ================================================ es: label_support_checkbox: "Enviar correo al usuario de soporte" email_was_send_to_supportclient: "Esta respuesta ha sido enviada al usuario de soporte." ================================================ FILE: config/locales/it.yml ================================================ it: label_support_checkbox: "Invia email al cliente" email_was_send_to_supportclient: "Questa risposta è stata inviata al cliente." ================================================ FILE: config/locales/ja.yml ================================================ ja: label_support_checkbox: "サポート顧客へメールを送信" email_was_send_to_supportclient: "この回答はサポート顧客へ送信されました。" ================================================ FILE: config/locales/pl.yml ================================================ pl: label_support_checkbox: "Wyślij wiadomość do klienta" email_was_send_to_supportclient: "Wiadomość została wysłana do klienta." ================================================ FILE: config/locales/ru.yml ================================================ ru: label_support_checkbox: "Отправить e-mail клиенту поддержки" email_was_send_to_supportclient: "Этот ответ был отправлен клиенту поддержки" permission_treat_user_as_supportclient: "Определять пользователя как клиента техподдержки" ================================================ FILE: config/locales/tr.yml ================================================ tr: label_support_checkbox: "Bu yanıtı destek isteyen müşteriye e-posta ile gönder" email_was_send_to_supportclient: "Bu yanıt destek talebi isteyen müşteriye de gönderildi." ================================================ FILE: config/locales/zh.yml ================================================ zh: label_support_checkbox: "发送反馈Email至客户" email_was_send_to_supportclient: "反馈邮件已发送至客户。" permission_treat_user_as_supportclient: "将用户作为客户" ================================================ FILE: db/migrate/001_create_custom_owner_email_field.rb ================================================ class CreateCustomOwnerEmailField < ActiveRecord::Migration[5.2] def self.up # fix PG:DuplicateColumn errors # https://github.com/jfqd/redmine_helpdesk/issues/66 unless column_exists? :journals, :send_to_owner add_column :journals, :send_to_owner, :boolean, :default => false c = CustomField.new( :name => 'owner-email', :editable => true, :field_format => 'string') c.type = 'IssueCustomField' # cannot be set by mass assignement! c.save Tracker.all.each do |t| execute "INSERT INTO custom_fields_trackers (custom_field_id,tracker_id) VALUES (#{c.id},#{t.id})" end end end def self.down c = CustomField.find_by_name('owner-email') execute "DELETE FROM custom_fields_trackers WHERE custom_field_id=#{c.id}" c.delete remove_column :journals, :send_to_owner end end ================================================ FILE: db/migrate/002_create_custom_fields_for_reply.rb ================================================ class CreateCustomFieldsForReply < ActiveRecord::Migration[5.2] def self.up c = CustomField.new( :name => 'helpdesk-first-reply', :editable => true, :visible => false, # do not show it on the project summary page :field_format => 'text') c.type = 'ProjectCustomField' # cannot be set by mass assignement! c.save d = CustomField.new( :name => 'helpdesk-email-footer', :editable => true, :visible => false, # do not show it on the project summary page :field_format => 'text') d.type = 'ProjectCustomField' # cannot be set by mass assignement! d.save end def self.down CustomField.find_by_name('helpdesk-first-reply').delete CustomField.find_by_name('helpdesk-email-footer').delete end end ================================================ FILE: db/migrate/003_create_custom_field_for_sender_email.rb ================================================ class CreateCustomFieldForSenderEmail < ActiveRecord::Migration[5.2] def self.up c = CustomField.new( :name => 'helpdesk-sender-email', :editable => true, :visible => true, :field_format => 'string') c.type = 'ProjectCustomField' # cannot be set by mass assignement! c.save end def self.down CustomField.find_by_name('helpdesk-sender-email').delete end end ================================================ FILE: db/migrate/004_create_custom_field_for_send_to_owner_default.rb ================================================ class CreateCustomFieldForSendToOwnerDefault < ActiveRecord::Migration[5.2] def self.up c = CustomField.new( :name => 'helpdesk-send-to-owner-default', :editable => true, :visible => false, # do not show it on the project summary page :field_format => 'bool') c.type = 'ProjectCustomField' # cannot be set by mass assignement! c.save end def self.down CustomField.find_by_name('helpdesk-send-to-owner-default').delete end end ================================================ FILE: db/migrate/005_add_treat_as_supportclient_to_anonymous.rb ================================================ class AddTreatAsSupportclientToAnonymous < ActiveRecord::Migration[5.2] def self.up anon_id = User.where(type: 'AnonymousUser').first.try(:id) || User.where('lastname LIKE ?', 'Anonymous').first.try(:id) || 4 role = Role.where(builtin: anon_id).first role.add_permission!(:treat_user_as_supportclient) unless role.nil? end def self.down Role.all.each do |r| r.remove_permission!(:treat_user_as_supportclient) end end end ================================================ FILE: db/migrate/006_append_footer_to_first_reply.rb ================================================ class AppendFooterToFirstReply < ActiveRecord::Migration[5.2] # Appends helpdesk-email-footer to helpdesk-first-reply to ensure backward # compatibility for updaters. See https://github.com/jfqd/redmine_helpdesk/issues/52 def self.up first_reply_cf_id = CustomField.find_by_name('helpdesk-first-reply').id footer_cf_id = CustomField.find_by_name('helpdesk-email-footer').id CustomValue.where(:custom_field_id => first_reply_cf_id).each do |first_reply_cv| if !first_reply_cv.value.nil? first_reply_with_footer = first_reply_cv.value first_reply_with_footer << "\n\n" first_reply_with_footer << CustomValue.where("custom_field_id=? and customized_id=?", footer_cf_id, first_reply_cv.customized_id).first.value CustomValue.update(first_reply_cv.id, :value => first_reply_with_footer) end end end end ================================================ FILE: db/migrate/007_create_custom_copy_to_field.rb ================================================ class CreateCustomCopyToField < ActiveRecord::Migration[5.2] def self.up c = CustomField.new( :name => 'copy-to', :editable => true, :field_format => 'string') c.type = 'IssueCustomField' # cannot be set by mass assignement! c.save Tracker.all.each do |t| execute "INSERT INTO custom_fields_trackers (custom_field_id,tracker_id) VALUES (#{c.id},#{t.id})" end end def self.down c = CustomField.find_by_name('copy-to') execute "DELETE FROM custom_fields_trackers WHERE custom_field_id=#{c.id}" c.delete end end ================================================ FILE: db/migrate/008_create_custom_field_cc_handling.rb ================================================ class CreateCustomFieldCcHandling < ActiveRecord::Migration[5.2] def self.up c = CustomField.new( :name => 'cc-handling', :editable => true, :visible => true, :field_format => 'bool') c.type = 'ProjectCustomField' # cannot be set by mass assignement! c.save end def self.down CustomField.find_by_name('cc-handling').delete end end ================================================ FILE: db/migrate/009_create_custom_field_for_reopen_closed_issues_by_email.rb ================================================ class CreateCustomFieldForReopenClosedIssuesByEmail < ActiveRecord::Migration[5.2] def self.up c = CustomField.new( :name => 'reopen-issues-with', :editable => true, :visible => true, :field_format => 'string') c.type = 'ProjectCustomField' # cannot be set by mass assignement! c.save end def self.down CustomField.find_by_name('reopen-issues-with').delete end end ================================================ FILE: db/migrate/010_create_custom_field_for_reply_separator.rb ================================================ class CreateCustomFieldForReplySeparator < ActiveRecord::Migration[5.2] def self.up begin c = CustomField.new( :name => 'helpdesk-reply-separator', :editable => true, :visible => false, # do not show it on the project summary page :field_format => 'string', :default_value => '-- Reply above this line --') c.type = 'ProjectCustomField' c.save rescue # rescue a possible migration error caused by renumbering the file end end def self.down CustomField.find_by_name('helpdesk-reply-separator').delete end end ================================================ FILE: init.rb ================================================ require 'redmine' $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/lib" require 'helpdesk_hooks' require 'helpdesk_mailer' require 'redmine_helpdesk/journal_patch' require 'redmine_helpdesk/mail_handler_patch' require 'redmine_helpdesk/mailer_patch' Redmine::Plugin.register :redmine_helpdesk do name 'Redmine helpdesk plugin' author 'Stefan Husch' description 'Redmine helpdesk plugin' version '0.0.20' requires_redmine :version_or_higher => '5.0.0' project_module :issue_tracking do permission :treat_user_as_supportclient, {} end end ================================================ FILE: lib/helpdesk_hooks.rb ================================================ class HelpdeskHooks < Redmine::Hook::Listener # render partial for 'Send mail to supportclient' def view_issues_edit_notes_bottom(context={}) i = Issue.find(context[:issue].id) c = CustomField.find_by_name('owner-email') owner_email = i.custom_value_for(c).try(:value) return if owner_email.blank? p = i.project s = CustomField.find_by_name('helpdesk-send-to-owner-default') send_to_owner_default = p.custom_value_for(s).try(:value) if p.present? && s.present? lookup = ActionView::LookupContext.new(File.dirname(__FILE__) + '/../app/views/') context = ActionView::Base.with_empty_template_cache.new(lookup, {}, nil) renderer = ActionView::Renderer.new(lookup) renderer.render( context, partial: "issue_edit", locals: { email: owner_email, send_to_owner_default: (send_to_owner_default.present? && send_to_owner_default || false) } ) end # fetch 'send_to_owner' param and set the value into journal.send_to_owner def controller_issues_edit_before_save(context={}) send_to_owner = (context[:params]['send_to_owner'] == "true") context[:journal].send_to_owner = send_to_owner end # add a history note on the journal def view_issues_history_journal_bottom(context={}) return if (context[:journal].nil? || context[:journal].notes.nil? || context[:journal].notes.length == 0) return unless context[:journal].send_to_owner == true i = Issue.find(context[:journal].journalized_id) c = CustomField.find_by_name('owner-email') owner_email = i.custom_value_for(c).try(:value) return if owner_email.blank? lookup = ActionView::LookupContext.new(File.dirname(__FILE__) + '/../app/views/') context = ActionView::Base.with_empty_template_cache.new(lookup, {}, nil) renderer = ActionView::Renderer.new(lookup) renderer.render( context, partial: "issue_history", locals: { email: owner_email } ) end end ================================================ FILE: lib/helpdesk_mailer.rb ================================================ # # With Rails 3 mail is send with the mail method. Sadly redmine # uses this method-name too in their mailer. This is the reason # why we need our own Mailer class. # class HelpdeskMailer < ActionMailer::Base helper :application include Redmine::I18n include MacroExpander # set the hostname for url_for helper def self.default_url_options { :host => Setting.host_name, :protocol => Setting.protocol } end # Sending email notifications to the supportclient def email_to_supportclient(issue, params) # issue, recipient, journal=nil, text='', copy_to=nil recipient = params[:recipient] journal = params[:journal] text = params[:text] carbon_copy = params[:carbon_copy] 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 message_id issue references issue subject = "[#{issue.project.name} - ##{issue.id}] #{issue.subject}" # Set 'from' email-address to 'helpdesk-sender-email' if available. # Falls back to regular redmine behaviour if 'sender' is empty. p = issue.project s = CustomField.find_by_name('helpdesk-sender-email') sender = p.custom_value_for(s).try(:value) if p.present? && s.present? # If a custom field with text for the first reply is # available then use this one instead of the regular r = CustomField.find_by_name('helpdesk-first-reply') f = CustomField.find_by_name('helpdesk-email-footer') reply = p.nil? || r.nil? ? '' : p.custom_value_for(r).try(:value) footer = p.nil? || f.nil? ? '' : p.custom_value_for(f).try(:value) # add carbon copy ct = CustomField.find_by_name('copy-to') if carbon_copy.nil? carbon_copy = issue.custom_value_for(ct).try(:value) end # add any attachements if journal.present? && text.present? journal.details.each do |d| if d.property == 'attachment' a = Attachment.find(d.prop_key) begin attachments[a.filename] = File.binread(a.diskfile) rescue # ignore rescue end end end end if @message_id_object headers[:message_id] = "<#{self.class.message_id_for(@message_id_object)}>" end if @references_objects headers[:references] = @references_objects.collect {|o| "<#{self.class.references_for(o)}>"}.join(' ') end # create mail object to deliver mail = if text.present? || reply.present? # sending out the journal note to the support client # or the first reply message t = text.present? ? "#{text}\n\n#{footer}" : reply body = expand_macros(t, issue, journal) # precess reply-separator f = CustomField.find_by_name('helpdesk-reply-separator') reply_separator = issue.project.custom_value_for(f).try(:value) if !reply_separator.blank? body = reply_separator + "\n\n" + body end mail( :from => sender.present? && sender || Setting.mail_from, :reply_to => sender.present? && sender || Setting.mail_from, :to => recipient, :subject => subject, :body => body, :date => Time.zone.now, :cc => carbon_copy ) else # fallback to a regular notifications email with redmine view @issue = issue @journal = journal @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue) mail( :from => sender.present? && sender || Setting.mail_from, :reply_to => sender.present? && sender || Setting.mail_from, :to => recipient, :subject => subject, :date => Time.zone.now, :template_path => 'mailer', :template_name => 'issue_edit', :cc => carbon_copy ) end # return mail object to deliver it return mail end private # Appends a Redmine header field (name is prepended with 'X-Redmine-') def redmine_headers(h) h.each { |k,v| headers["X-Redmine-#{k}"] = v.to_s } end def self.token_for(object, rand=true) timestamp = object.send(object.respond_to?(:created_on) ? :created_on : :updated_on) hash = [ "redmine", "#{object.class.name.demodulize.underscore}-#{object.id}", timestamp.strftime("%Y%m%d%H%M%S") ] if rand hash << Redmine::Utils.random_hex(8) end host = Setting.mail_from.to_s.strip.gsub(%r{^.*@|>}, '') host = "#{::Socket.gethostname}.redmine" if host.empty? "#{hash.join('.')}@#{host}" end # Returns a Message-Id for the given object def self.message_id_for(object) token_for(object, true) end # Returns a uniq token for a given object referenced by all notifications # related to this object def self.references_for(object) token_for(object, false) end def message_id(object) @message_id_object = object end def references(object) @references_objects ||= [] @references_objects << object end end ================================================ FILE: lib/macro_expander.rb ================================================ module MacroExpander def expand_macros(string, issue, journal) e = Expander.new(string, issue, journal) e.expand end class Expander include Redmine::I18n def initialize(string, issue, journal) @string = string @issue = issue @journal = journal end def expand unless @issue.nil? expand_issue expand_project end expand_user unless @journal.nil? expand_base @string end private def expand_issue @string.gsub!("##issue-id##", @issue.id.to_s) @string.gsub!("##issue-subject##", @issue.subject) @string.gsub!("##issue-tracker##", @issue.tracker.name) @string.gsub!("##issue-status##", @issue.status.name) end def expand_project p = @issue.project @string.gsub!("##project-name##", p.name) end def expand_user u = @journal.user @string.gsub!("##user-name##", u.name) @string.gsub!("##user-firstname##", u.firstname) @string.gsub!("##user-lastname##", u.lastname) @string.gsub!("##user-mail##", u.mail) @string.gsub!("##user-login##", u.login) expand_user_cf(u) end def expand_user_cf(user) CustomField.where( "type = 'UserCustomField'").each do |user_cf| cf_name = user_cf.name.downcase.gsub(' ', '-') user_cf_cv = user.custom_value_for(user_cf).try(:value) @string.gsub!("##user-cf-#{cf_name}##", user_cf_cv) unless user_cf_cv.nil? end end def expand_base @string.gsub!("##time-now##", I18n.l(Time.zone.now)) end end end ================================================ FILE: lib/redmine_helpdesk/journal_patch.rb ================================================ module RedmineHelpdesk module JournalPatch def self.included(base) # :nodoc: base.send(:include, InstanceMethods) base.class_eval do alias_method :send_notification_without_helpdesk, :send_notification alias_method :send_notification, :send_notification_with_helpdesk end end module InstanceMethods # Overrides the send_notification method which # is only called on journal updates def send_notification_with_helpdesk if notify? && (Setting.notified_events.include?('issue_updated') || (Setting.notified_events.include?('issue_note_added') && notes.present?) || (Setting.notified_events.include?('issue_status_updated') && new_status.present?) || (Setting.notified_events.include?('issue_assigned_to_updated') && detail_for_attribute('assigned_to_id').present?) || (Setting.notified_events.include?('issue_priority_updated') && new_value_for('priority_id').present?) ) Mailer.deliver_issue_edit(self) end # do not send on private-notes if private_notes == true && send_to_owner == true self.send_to_owner = false self.save( validate: false ) end # sending email notifications to the supportclient # only if the send_to_owner checkbox was checked if send_to_owner == true && notes.length != 0 issue = self.journalized.reload owner_email = issue.custom_value_for( CustomField.find_by_name('owner-email') ).value HelpdeskMailer.email_to_supportclient( issue, { recipient: owner_email, journal: self, text: notes } ).deliver unless owner_email.blank? end end end # module InstanceMethods end # module JournalPatch end # module RedmineHelpdesk # Add module to Journal class Journal.send(:include, ::RedmineHelpdesk::JournalPatch) ================================================ FILE: lib/redmine_helpdesk/mail_handler_patch.rb ================================================ module RedmineHelpdesk module MailHandlerPatch def self.included(base) # :nodoc: base.send(:include, InstanceMethods) base.class_eval do alias_method :dispatch_to_default_without_helpdesk, :dispatch_to_default alias_method :dispatch_to_default, :dispatch_to_default_with_helpdesk # needed for reopening a closed issue alias_method :receive_issue_reply_without_helpdesk, :receive_issue_reply alias_method :receive_issue_reply, :receive_issue_reply_with_helpdesk end end module InstanceMethods private # Overrides the dispatch_to_default method to # set the owner-email of a new issue created by # an email request def dispatch_to_default_with_helpdesk issue = receive_issue issue.reload # prevent ActiveRecord::StaleObjectError roles = if issue.author.class == AnonymousUser Role.where(builtin: issue.author.id) else issue.author.roles_for_project(issue.project) end # add owner-email only if the author has assigned some role with # permission treat_user_as_supportclient enabled if issue.author.type.eql?("AnonymousUser") || roles.any? {|role| role.allowed_to?(:treat_user_as_supportclient) } sender_email = @email.from.first # any cc handling needed? custom_value = custom_field_value(issue.project,'cc-handling') if (!@email.cc.nil?) && (custom_value.value == '1') carbon_copy = @email[:cc].formatted.join(', ') custom_value = custom_field_value(issue,'copy-to') custom_value.value = carbon_copy custom_value.save( validate: false ) # skip validation! else carbon_copy = nil end issue.description = email_details + issue.description issue.save( validate: false ) # skip validation! custom_value = custom_field_value(issue,'owner-email') if custom_value.value.to_s.strip.empty? custom_value.value = sender_email custom_value.save( validate: false ) # skip validation! else # Email owner field was already set by some preprocess hooks. # So now we need to send message to another recepient. sender_email = custom_value.value.to_s.strip end # regular email sending to known users is done # on the first issue.save. So we need to send # the notification email to the supportclient # on our own. HelpdeskMailer.email_to_supportclient( issue, { recipient: sender_email, carbon_copy: carbon_copy } ).deliver end after_dispatch_to_default_hook issue return issue end # let other plugins the chance to override this # method to hook into dispatch_to_default def after_dispatch_to_default_hook(issue) end # Fix an issue with email.has_attachments? def add_attachments(obj) if !email.attachments.nil? && email.attachments.size > 0 email.attachments.each do |attachment| obj.attachments << Attachment.create( container: obj, file: attachment.decoded, filename: attachment.filename, author: user, content_type: attachment.mime_type ) end end end # Overrides the receive_issue_reply method def receive_issue_reply_with_helpdesk(issue_id, from_journal=nil) issue = Issue.find_by_id(issue_id) return unless issue # reopening a closed issues by email custom_value = custom_field_value(issue.project,'reopen-issues-with') if issue.closed? && custom_value.present? && custom_value.value.present? status_id = IssueStatus.where("name = ?", custom_value.value).try(:first).try(:id) unless status_id.nil? issue.status_id = status_id issue.save end end # call original method receive_issue_reply_without_helpdesk(issue_id, from_journal) # store email-details before each note last_journal = Journal.find(issue.last_journal_id) last_journal.notes = email_details + last_journal.notes last_journal.save return last_journal end def custom_field_value(issue,name) custom_field = CustomField.find_by_name(name) CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.id, custom_field.id ).first end def email_details details = "From: " + @email[:from].formatted.first + "\n" details << "To: " + @email[:to].formatted.join(', ') + "\n" if !@email.to.nil? details << "Cc: " + @email[:cc].formatted.join(', ') + "\n" if !@email.cc.nil? details << "Date: " + @email[:date].to_s + "\n" "
\n" + Mail::Encodings.unquote_and_convert_to(details, 'utf-8') + "
\n\n" end end # module InstanceMethods end # module MailHandlerPatch end # module RedmineHelpdesk # Add module to MailHandler class MailHandler.send(:include, ::RedmineHelpdesk::MailHandlerPatch) ================================================ FILE: lib/redmine_helpdesk/mailer_patch.rb ================================================ module RedmineHelpdesk module MailerPatch def self.included(base) # :nodoc: base.send(:include, InstanceMethods) base.class_eval do alias_method :issue_edit_without_helpdesk, :issue_edit alias_method :issue_edit, :issue_edit_with_helpdesk end end module InstanceMethods # Overrides the issue_edit method which is only # be called on existing tickets. We will add the # owner-email to the recipients only if no email- # footer text is available. def issue_edit_with_helpdesk(user, journal) issue = journal.journalized redmine_headers 'Project' => issue.project.identifier, 'Issue-Id' => issue.id, 'Issue-Author' => issue.author.login, 'Issue-Tracker' => issue.tracker redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to message_id journal references issue @author = journal.user # process reply-separator f = CustomField.find_by_name('helpdesk-reply-separator') reply_separator = issue.project.custom_value_for(f).try(:value) if !reply_separator.blank? and !journal.notes.nil? journal.notes = journal.notes.gsub(/#{reply_separator}.*/m, '') journal.save(:validate => false) end # add owner-email to the recipients alternative_user = nil begin if journal.send_to_owner == true f = CustomField.find_by_name('helpdesk-email-footer') p = issue.project owner_email = issue.custom_value_for( CustomField.find_by_name('owner-email') ).value if !owner_email.blank? && !f.nil? && !p.nil? && p.custom_value_for(f).try(:value).blank? alternative_user = owner_email end end rescue Exception => e Rails.logger.error "Error while adding owner-email to recipients of email notification: \"#{e.message}\"." end # any cc handling needed? cc_users = nil begin # any cc handling needed? if alternative_user.present? custom_field = CustomField.find_by_name('cc-handling') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, custom_field.id ).first cc_users = custom_value.value.split(',').map(&:strip) if custom_value.value.present? end rescue Exception => e Rails.logger.error "Error while adding cc-users to recipients of email notification: \"#{e.message}\"." end s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] " s += "(#{issue.status.name}) " if journal.new_value_for('status_id') && Setting.show_status_changes_in_mail_subject? s += issue.subject u = (alternative_user.present? ? alternative_user : user) @issue = issue @user = u @journal = journal @journal_details = journal.visible_details @issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue, :anchor => "change-#{journal.id}") mail( :to => u, :cc => cc_users, :subject => s ) end end # module InstanceMethods end # module MailerPatch end # module RedmineHelpdesk # Add module to Mailer class Mailer.send(:include, ::RedmineHelpdesk::MailerPatch) ================================================ FILE: lib/tasks/local-db.rake ================================================ namespace :helpdesk do namespace :localdb do db_label = 'helpdesk-db' def get_env () db_name = 'redmine' db_host = 'localhost' db_user = 'root' db_adapter = ENV['DATABASE_ADAPTER'] case db_adapter when "mysql" env = "-e MYSQL_DATABASE=#{db_name} "\ "-e MYSQL_ALLOW_EMPTY_PASSWORD=yes " when "postgresql" env = "-e POSTGRES_DB=#{db_name} "\ "-e POSTGRES_USER=#{db_user} "\ when "postgresql_ext" env = "-e POSTGRES_DB=#{db_name} "\ "-e POSTGRES_USER=#{db_user}" else raise StandardError.new "Cannot start local db" end env end def get_port () db_adapter = ENV['DATABASE_ADAPTER'] case db_adapter when "mysql" port = 3306 when "postgresql" port = 5432 when "postgresql_ext" port = 5432 else raise StandardError.new "Cannot start local db" end port end def get_image () db_adapter = ENV['DATABASE_ADAPTER'] case db_adapter when "mysql" image = "mysql" when "postgresql" image = "postgres" when "postgresql_ext" image = "postgres" else raise StandardError.new "Cannot start local db" end image end task :start => :stop do env = get_env() port = get_port() image = get_image() cmd = "docker run -d --name #{db_label} "\ "--net=host "\ "#{env} "\ "#{image}" puts cmd system(cmd) end task :stop do system("docker stop #{db_label}") system("docker rm #{db_label}") end end end ================================================ FILE: lib/tasks/plugin_ci.rake ================================================ namespace :helpdesk do plugin_root = File.expand_path('../../../', __FILE__) coverage_dir = "#{plugin_root}/coverage" desc 'Runs :ci target from plugin scope in test/app' task :ext_ci do cd('test/app') do sh('bundle exec rake helpdesk:ci') end end desc 'Prepared and configures DB in Docker container' task :prepare_local => ['helpdesk:localdb:start', 'helpdesk:redmine:configure_db'] do Rake::Task['helpdesk:install'].invoke() end desc 'Runs bundle install from plugin scop in test/app' task :install do cd('test/app') do sh('bundle install --without production development rmagick') end end desc 'Runs migrate from plugin scope in test/app' task :migrate do cd('test/app') do sh('bundle exec rake generate_secret_token') sh('bundle exec rake db:migrate') end end desc 'Prepares and runs the test suite.' task :ci => ['redmine:plugins:migrate', 'helpdesk:test'] desc 'Runs Redmine plugin tests with single CodeClimate TestReporter invocation.' task :test => [ 'helpdesk:test:clear_coverage_data', 'helpdesk:test:run' ] namespace :test do task :run do Rake::Task['redmine:plugins:test:units'].invoke Rake::Task['redmine:plugins:test:functionals'].invoke # require "simplecov" # require "codeclimate-test-reporter" # SimpleCov.coverage_dir "plugins/coverage" # CodeClimate::TestReporter.configure do |config| # config.git_dir = "plugins/#{ENV['GITHUB_PROJECT']}" # end # CodeClimate::TestReporter::Formatter.new.format(SimpleCov.result) end task :clear_coverage_data do rm_rf coverage_dir end end end ================================================ FILE: lib/tasks/redmine.rake ================================================ require 'fileutils' namespace :helpdesk do namespace :redmine do plugin_root = File.expand_path('../../../', __FILE__) redmine_path = "#{plugin_root}/test/app" tmp_dir = "#{plugin_root}/tmp" redmine_source_url = "http://www.redmine.org/releases" redmine_version = ENV['REDMINE_VERSION'] || "3.2.0" redmine_name = "redmine-#{redmine_version}" redmine_package = "#{redmine_name}.tar.gz" redmine_url = "#{redmine_source_url}/#{redmine_package}" version = redmine_version.split(".") major = version[0] minor = version[1] patch = version[2] task :install => [ :remove, :print_env, :download_tarball, :extract_tarball, :configure_db, :link_plugin, :print_result ] task :print_env do if ENV['DATABASE_ADAPTER'].nil? raise StandardError.new "Database adapter not defined" end puts "" puts "######################" puts "REDMINE INSTALLATION SCRIPT" puts "" puts "REDMINE_VERSION : #{redmine_version}" puts "REDMINE_URL : #{redmine_url}" puts "DATABASE ADAPTER : #{ENV['DATABASE_ADAPTER']}" puts "" end task :remove do rm_rf redmine_path end task :download_tarball do mkdir_p tmp_dir Dir.chdir(tmp_dir) do puts "Downloading tarball" if !system("wget #{redmine_url}") raise StandardError.new "download failed" end end end task :extract_tarball do Dir.chdir(tmp_dir) do puts "Extracting tarball" if !system("tar xf #{redmine_package}") raise StandardError.new "download failed" end mv "#{redmine_name}", "#{redmine_path}", :force => true rm_rf tmp_dir end end task :configure_db do puts "Configuring database" db_adapter = ENV['DATABASE_ADAPTER'] case db_adapter when "mysql" a = "mysql" when "postgresql" a = "postgres" when "postgresql_ext" a = "postgres_ext" when "sqlite" a = "sqlite" else raise StandardError.new "Error copying config files" end cp "#{plugin_root}/test/confs/database_#{a}.yml", "#{redmine_path}/config/database.yml", :verbose => true end task :print_result do puts "Dummy Redmine dir listing" Dir["#{redmine_path}/*"].each do |f| puts f.sub("#{redmine_path}", "") end puts "" puts "Dummy Redmine plugin dir listing" Dir["#{redmine_path}/plugins/*"].each do |f| puts f.sub("#{redmine_path}/plugins", "") end puts "" end task :link_plugin do plugin_dir = "#{redmine_path}/plugins/redmine_helpdesk" ln_sf plugin_root, plugin_dir end end end ================================================ FILE: test/confs/database_mysql.yml ================================================ ## MySQL configuration example ## Data come from environment variables so the test suite can be run ## on Travis or Jenkins (see https://github.com/codevise/jenkins-mysql-job-databases-plugin) production: adapter: mysql2 database: <%= ENV['MYSQL_DATABASE'] || 'redmine' %> host: <%= ENV['MYSQL_HOST'] || '127.0.0.1' %> port: <%= ENV['MYSQL_PORT'] || '3306' %> username: <%= ENV['MYSQL_USER'] || 'root' %> password: <%= ENV['MYSQL_PASSWORD'] %> encoding: utf8 development: adapter: mysql2 database: <%= ENV['MYSQL_DATABASE'] || 'redmine' %> host: <%= ENV['MYSQL_HOST'] || '127.0.0.1' %> port: <%= ENV['MYSQL_PORT'] || '3306' %> username: <%= ENV['MYSQL_USER'] || 'root' %> password: <%= ENV['MYSQL_PASSWORD'] %> encoding: utf8 test: adapter: mysql2 database: <%= ENV['MYSQL_DATABASE'] || 'redmine' %> host: <%= ENV['MYSQL_HOST'] || '127.0.0.1' %> port: <%= ENV['MYSQL_PORT'] || '3306' %> username: <%= ENV['MYSQL_USER'] || 'root' %> password: <%= ENV['MYSQL_PASSWORD'] %> encoding: utf8 ================================================ FILE: test/confs/database_postgres.yml ================================================ ## PostgreSQL configuration example ## Data come from environment variables so the test suite can be run ## on Travis or Jenkins (see https://github.com/lmlima/jenkins-postgresql-job-databases-plugin) production: adapter: postgresql database: <%= ENV['POSTGRES_DATABASE'] || 'redmine' %> username: <%= ENV['POSTGRES_USER'] || 'root' %> encoding: utf8 development: adapter: postgresql database: <%= ENV['POSTGRES_DATABASE'] || 'redmine' %> username: <%= ENV['POSTGRES_USER'] || 'root' %> encoding: utf8 test: adapter: postgresql database: <%= ENV['POSTGRES_DATABASE'] || 'redmine' %> username: <%= ENV['POSTGRES_USER'] || 'root' %> encoding: utf8 ================================================ FILE: test/confs/database_postgres_ext.yml ================================================ ## PostgreSQL configuration example ## Data come from environment variables so the test suite can be run ## on Travis or Jenkins (see https://github.com/lmlima/jenkins-postgresql-job-databases-plugin) production: adapter: postgresql database: <%= ENV['POSTGRES_DATABASE'] || 'redmine' %> host: <%= ENV['POSTGRES_HOST'] || 'localhost' %> username: <%= ENV['POSTGRES_USER'] || 'root' %> encoding: utf8 development: adapter: postgresql database: <%= ENV['POSTGRES_DATABASE'] || 'redmine' %> host: <%= ENV['POSTGRES_HOST'] || 'localhost' %> username: <%= ENV['POSTGRES_USER'] || 'root' %> encoding: utf8 test: adapter: postgresql database: <%= ENV['POSTGRES_DATABASE'] || 'redmine' %> host: <%= ENV['POSTGRES_HOST'] || 'localhost' %> username: <%= ENV['POSTGRES_USER'] || 'root' %> encoding: utf8 ================================================ FILE: test/confs/database_sqlite.yml ================================================ ## SQLite3 configuration example production: adapter: sqlite3 database: db/redmine.sqlite3 development: adapter: sqlite3 database: db/redmine.dev.sqlite3 test: adapter: sqlite3 database: db/redmine.test.sqlite3 ================================================ FILE: test/fixtures/2.6/attachments.yml ================================================ --- attachments_001: created_on: 2006-07-19 21:07:27 +02:00 container_type: Issue container_id: 1 downloads: 0 disk_filename: 060719210727_source.rb disk_directory: "2006/07" digest: b91e08d0cf966d5c6ff411bd8c4cc3a2 id: 1 filesize: 153 filename: source.rb author_id: 1 description: This is a Ruby source file content_type: application/x-ruby ================================================ FILE: test/fixtures/2.6/custom_fields.yml ================================================ --- custom_fields_001: name: owner-email regexp: "" type: IssueCustomField possible_values: "" id: 1 is_required: false field_format: string default_value: "" editable: true visible: true position: 1 custom_fields_002: name: helpdesk-first-reply min_length: 1 regexp: "" visible: false type: ProjectCustomField max_length: 100 possible_values: "" id: 2 is_required: false field_format: text default_value: "" editable: true position: 1 custom_fields_003: name: helpdesk-email-footer min_length: 1 regexp: "" visible: false type: ProjectCustomField max_length: 100 possible_values: "" id: 3 is_required: false field_format: text default_value: "" editable: true position: 1 custom_fields_004: name: helpdesk-sender-email regexp: "" type: ProjectCustomField possible_values: "" id: 4 is_required: false field_format: string default_value: false editable: true visible: true position: 1 custom_fields_005: name: helpdesk-send-to-owner-default regexp: "" type: ProjectCustomField possible_values: "" id: 5 is_required: false field_format: bool default_value: false editable: true visible: false position: 1 custom_fields_006: name: helpdesk-subject min_length: 1 regexp: "" visible: false type: ProjectCustomField max_length: 100 possible_values: "" id: 6 is_required: false field_format: text default_value: "" editable: true position: 1 custom_fields_007: name: helpdesk-send-first-reply visible: false type: ProjectCustomField id: 7 is_required: false field_format: boolean default_value: true editable: true position: 1 custom_fields_008: name: title regexp: "" is_for_all: false type: UserCustomField possible_values: "" id: 8 is_required: false field_format: string default_value: "" editable: true position: 1 custom_fields_009: name: motto regexp: "" is_for_all: false type: UserCustomField possible_values: "" id: 9 is_required: false field_format: string default_value: "" editable: true position: 2 custom_fields_010: name: copy-to regexp: "" type: IssueCustomField possible_values: "" id: 10 is_required: false field_format: string default_value: "" editable: true visible: true position: 1 custom_fields_011: name: cc-handling visible: false type: ProjectCustomField id: 11 is_required: false field_format: bool default_value: false editable: true position: 1 ================================================ FILE: test/fixtures/2.6/custom_fields_projects.yml ================================================ --- custom_fields_projects_001: custom_field_id: 1 project_id: 1 custom_fields_projects_002: custom_field_id: 1 project_id: 2 custom_fields_projects_003: custom_field_id: 10 project_id: 1 custom_fields_projects_004: custom_field_id: 10 project_id: 2 custom_fields_projects_005: custom_field_id: 1 project_id: 4 custom_fields_projects_006: custom_field_id: 10 project_id: 4 ================================================ FILE: test/fixtures/2.6/custom_fields_trackers.yml ================================================ --- custom_fields_trackers_001: custom_field_id: 1 tracker_id: 1 custom_fields_trackers_002: custom_field_id: 1 tracker_id: 2 custom_fields_trackers_003: custom_field_id: 1 tracker_id: 3 custom_fields_trackers_004: custom_field_id: 1 tracker_id: 4 custom_fields_trackers_005: custom_field_id: 10 tracker_id: 1 custom_fields_trackers_006: custom_field_id: 10 tracker_id: 2 custom_fields_trackers_007: custom_field_id: 10 tracker_id: 3 custom_fields_trackers_008: custom_field_id: 10 tracker_id: 4 ================================================ FILE: test/fixtures/2.6/custom_values.yml ================================================ --- custom_values_001: customized_type: Project custom_field_id: 2 customized_id: 1 id: 1 value: "first reply" custom_values_002: customized_type: Project custom_field_id: 3 customized_id: 1 id: 2 value: "email footer" custom_values_003: customized_type: Project custom_field_id: 4 customized_id: 1 id: 3 value: reply@example.com custom_values_004: customized_type: Project custom_field_id: 5 customized_id: 1 id: 4 value: 0 custom_values_005: customized_type: Project custom_field_id: 11 customized_id: 1 id: 5 value: 1 custom_values_006: customized_type: Project custom_field_id: 2 customized_id: 2 id: 6 value: first reply custom_values_007: customized_type: Project custom_field_id: 3 customized_id: 2 id: 7 value: email footer custom_values_008: customized_type: Project custom_field_id: 4 customized_id: 2 id: 8 value: reply@example.com custom_values_009: customized_type: Project custom_field_id: 5 customized_id: 2 id: 9 value: 0 custom_values_010: customized_type: Project custom_field_id: 11 customized_id: 2 id: 10 value: 1 custom_values_011: customized_type: Issue custom_field_id: 1 customized_id: 1 id: 11 value: owner@example.com custom_values_012: customized_type: Project custom_field_id: 2 customized_id: 4 id: 12 value: first reply custom_values_013: customized_type: Project custom_field_id: 3 customized_id: 4 id: 13 value: email footer custom_values_014: customized_type: Project custom_field_id: 4 customized_id: 4 id: 14 value: reply@example.com custom_values_015: customized_type: Project custom_field_id: 5 customized_id: 4 id: 15 value: 0 custom_values_016: customized_type: Project custom_field_id: 11 customized_id: 4 id: 16 value: 0 ================================================ FILE: test/fixtures/2.6/enabled_modules.yml ================================================ --- enabled_modules_001: name: issue_tracking project_id: 1 id: 1 enabled_modules_002: name: time_tracking project_id: 1 id: 2 enabled_modules_003: name: files project_id: 1 id: 3 enabled_modules_004: name: issue_tracking project_id: 2 id: 4 enabled_modules_005: name: time_tracking project_id: 2 id: 5 enabled_modules_006: name: files project_id: 2 id: 6 enabled_modules_007: name: issue_tracking project_id: 4 id: 7 enabled_modules_008: name: time_tracking project_id: 4 id: 8 enabled_modules_009: name: files project_id: 4 id: 9 ================================================ FILE: test/fixtures/2.6/enumerations.yml ================================================ --- enumerations_001: name: Low id: 1 type: IssuePriority active: true position: 1 position_name: lowest enumerations_002: name: Normal id: 2 type: IssuePriority is_default: true active: true position: 2 position_name: default enumerations_003: name: High id: 3 type: IssuePriority active: true position: 3 position_name: high3 enumerations_004: name: Urgent id: 4 type: IssuePriority active: true position: 4 position_name: high2 enumerations_005: name: Immediate id: 5 type: IssuePriority active: true position: 5 position_name: highest ================================================ FILE: test/fixtures/2.6/issue_statuses.yml ================================================ --- issue_statuses_001: id: 1 name: New is_default: true is_closed: false position: 1 issue_statuses_002: id: 2 name: Assigned is_default: false is_closed: false position: 2 issue_statuses_003: id: 3 name: Resolved is_default: false is_closed: false position: 3 issue_statuses_004: name: Feedback id: 4 is_default: false is_closed: false position: 4 issue_statuses_005: id: 5 name: Closed is_default: false is_closed: true position: 5 issue_statuses_006: id: 6 name: Rejected is_default: false is_closed: true position: 6 ================================================ FILE: test/fixtures/2.6/issues.yml ================================================ --- issues_001: created_on: 2015-03-05 21:41:21 +02:00 project_id: 1 updated_on: 2015-03-05 22:05:50 +02:00 priority_id: 2 subject: Can't print recipes id: 1 fixed_version_id: category_id: 1 description: Unable to print recipes tracker_id: 1 assigned_to_id: 1 author_id: 1 status_id: 1 start_date: due_date: root_id: 1 lft: 1 rgt: 2 lock_version: 3 issues_002: created_on: 2006-07-19 21:04:21 +02:00 project_id: 2 updated_on: 2006-07-19 21:09:50 +02:00 priority_id: 2 subject: Add ingredients categories id: 2 fixed_version_id: category_id: description: Ingredients of the recipe should be classified by categories tracker_id: 2 assigned_to_id: author_id: 1 status_id: 2 start_date: due_date: root_id: 2 lft: 1 rgt: 2 lock_version: 3 ================================================ FILE: test/fixtures/2.6/journal_details.yml ================================================ --- journal_details_001: old_value: property: attachment id: 1 value: 060719210727_source.rb prop_key: 1 journal_id: 3 ================================================ FILE: test/fixtures/2.6/journals.yml ================================================ --- journals_001: created_on: <%= 2.days.ago.to_date.to_s(:db) %> notes: "Journal notes" id: 1 journalized_type: Issue user_id: 1 journalized_id: 1 journals_002: created_on: <%= 1.days.ago.to_date.to_s(:db) %> notes: "Some notes." id: 2 journalized_type: Issue user_id: 1 journalized_id: 1 journals_003: created_on: <%= 1.days.ago.to_date.to_s(:db) %> notes: "Some notes with an attachment." id: 3 journalized_type: Issue user_id: 1 journalized_id: 1 journals_004: id: 4 created_on: <%= 1.days.ago.to_date.to_s(:db) %> notes: "A comment on an issue." user_id: 1 journalized_type: Issue journalized_id: 2 ================================================ FILE: test/fixtures/2.6/member_roles.yml ================================================ --- member_roles_001: id: 1 role_id: 1 member_id: 1 member_roles_002: id: 2 role_id: 1 member_id: 2 member_roles_003: id: 3 role_id: 1 member_id: 3 member_roles_004: id: 4 role_id: 6 member_id: 4 ================================================ FILE: test/fixtures/2.6/members.yml ================================================ --- members_001: created_on: 2006-07-19 19:35:33 +02:00 project_id: 1 id: 1 user_id: 1 mail_notification: true members_002: created_on: 2006-07-19 19:35:36 +02:00 project_id: 2 id: 2 user_id: 1 mail_notification: true members_003: created_on: 2006-07-19 19:35:36 +02:00 project_id: 3 id: 3 user_id: 1 mail_notification: true members_004: created_on: 2006-07-19 19:35:36 +02:00 project_id: 2 id: 4 user_id: 2 mail_notification: false members_005: created_on: 2006-07-19 19:35:36 +02:00 project_id: 4 id: 5 user_id: 1 mail_notification: true ================================================ FILE: test/fixtures/2.6/projects.yml ================================================ --- projects_001: created_on: 2015-03-05 19:13:59 +02:00 name: Helpdesk Project 1 updated_on: 2015-03-05 22:53:01 +02:00 id: 1 description: First helpdesk project homepage: "" is_public: true identifier: helpdesk_project_1 parent_id: lft: 1 rgt: 2 projects_002: created_on: 2015-03-05 19:14:19 +02:00 name: Helpdesk Project 2 updated_on: 2015-03-05 19:14:19 +02:00 id: 2 description: Second helpdesk project homepage: "" is_public: false identifier: helpdesk_project_2 parent_id: lft: 3 rgt: 4 projects_003: created_on: 2015-03-05 19:15:21 +02:00 name: Irrelevant Project updated_on: 2015-03-05 19:18:12 +02:00 id: 3 description: Project that has no helpdesk features homepage: "" is_public: true identifier: irrelevant_project parent_id: lft: 5 rgt: 6 projects_004: created_on: 2015-03-05 19:14:19 +02:00 name: Helpdesk Project 4 updated_on: 2015-03-05 19:14:19 +02:00 id: 4 description: Disabled copy-to helpdesk project homepage: "" is_public: true identifier: helpdesk_project_4 parent_id: lft: 6 rgt: 7 ================================================ FILE: test/fixtures/2.6/projects_trackers.yml ================================================ --- projects_trackers_001: project_id: 1 tracker_id: 1 projects_trackers_002: project_id: 1 tracker_id: 2 projects_trackers_003: project_id: 1 tracker_id: 3 projects_trackers_004: project_id: 2 tracker_id: 1 projects_trackers_005: project_id: 2 tracker_id: 2 projects_trackers_006: project_id: 2 tracker_id: 3 projects_trackers_007: project_id: 3 tracker_id: 1 projects_trackers_008: project_id: 3 tracker_id: 2 projects_trackers_009: project_id: 3 tracker_id: 3 projects_trackers_010: project_id: 4 tracker_id: 1 projects_trackers_011: project_id: 4 tracker_id: 2 projects_trackers_012: project_id: 4 tracker_id: 3 ================================================ FILE: test/fixtures/2.6/roles.yml ================================================ --- roles_001: name: Manager id: 1 builtin: 0 issues_visibility: all permissions: | --- - :add_project - :edit_project - :close_project - :select_project_modules - :manage_members - :manage_versions - :manage_categories - :view_issues - :add_issues - :edit_issues - :manage_issue_relations - :manage_subtasks - :add_issue_notes - :move_issues - :delete_issues - :view_issue_watchers - :add_issue_watchers - :set_issues_private - :set_notes_private - :view_private_notes - :delete_issue_watchers - :manage_public_queries - :save_queries - :view_gantt - :view_calendar - :log_time - :view_time_entries - :edit_time_entries - :delete_time_entries - :manage_news - :comment_news - :view_documents - :add_documents - :edit_documents - :delete_documents - :view_wiki_pages - :export_wiki_pages - :view_wiki_edits - :edit_wiki_pages - :delete_wiki_pages_attachments - :protect_wiki_pages - :delete_wiki_pages - :rename_wiki_pages - :add_messages - :edit_messages - :delete_messages - :manage_boards - :view_files - :manage_files - :browse_repository - :manage_repository - :view_changesets - :manage_related_issues - :manage_project_activities position: 1 roles_002: name: Developer id: 2 builtin: 0 issues_visibility: default permissions: | --- - :edit_project - :manage_members - :manage_versions - :manage_categories - :view_issues - :add_issues - :edit_issues - :manage_issue_relations - :manage_subtasks - :add_issue_notes - :move_issues - :delete_issues - :view_issue_watchers - :save_queries - :view_gantt - :view_calendar - :log_time - :view_time_entries - :edit_own_time_entries - :manage_news - :comment_news - :view_documents - :add_documents - :edit_documents - :delete_documents - :view_wiki_pages - :view_wiki_edits - :edit_wiki_pages - :protect_wiki_pages - :delete_wiki_pages - :add_messages - :edit_own_messages - :delete_own_messages - :manage_boards - :view_files - :manage_files - :browse_repository - :view_changesets position: 2 roles_003: name: Reporter id: 3 builtin: 0 issues_visibility: default permissions: | --- - :edit_project - :manage_members - :manage_versions - :manage_categories - :view_issues - :add_issues - :edit_issues - :manage_issue_relations - :add_issue_notes - :move_issues - :view_issue_watchers - :save_queries - :view_gantt - :view_calendar - :log_time - :view_time_entries - :manage_news - :comment_news - :view_documents - :add_documents - :edit_documents - :delete_documents - :view_wiki_pages - :view_wiki_edits - :edit_wiki_pages - :delete_wiki_pages - :add_messages - :manage_boards - :view_files - :manage_files - :browse_repository - :view_changesets position: 3 roles_004: name: Non member id: 4 builtin: 1 issues_visibility: default permissions: | --- - :view_issues - :add_issues - :edit_issues - :manage_issue_relations - :add_issue_notes - :save_queries - :view_gantt - :view_calendar - :log_time - :view_time_entries - :comment_news - :view_documents - :view_wiki_pages - :view_wiki_edits - :edit_wiki_pages - :add_messages - :view_files - :manage_files - :browse_repository - :view_changesets position: 4 roles_005: name: Anonymous id: 5 builtin: 2 issues_visibility: default permissions: | --- - :treat_user_as_supportclient - :view_issues - :add_issue_notes - :view_gantt - :view_calendar - :view_time_entries - :view_documents - :view_wiki_pages - :view_wiki_edits - :view_files - :browse_repository - :view_changesets position: 5 roles_006: name: SupportClient id: 6 builtin: 0 issues_visibility: default permissions: | --- - :treat_user_as_supportclient - :add_issues - :add_issue_notes position: 6 ================================================ FILE: test/fixtures/2.6/trackers.yml ================================================ --- trackers_001: name: Bug id: 1 is_in_chlog: true position: 1 trackers_002: name: Feature request id: 2 is_in_chlog: true position: 2 trackers_003: name: Support request id: 3 is_in_chlog: false position: 3 ================================================ FILE: test/fixtures/2.6/users.yml ================================================ --- users_001: created_on: 2015-03-05 19:34:07 +02:00 status: 1 last_login_on: language: en # password = foo salt: 3126f764c3c5ac61cbfc103f25f934cf hashed_password: 9e4dd7eeb172c12a0691a6d9d3a269f7e9fe671b updated_on: 2015-03-05 19:34:07 +02:00 admin: true mail: rhill@somenet.foo lastname: Hill firstname: Robert id: 1 auth_source_id: mail_notification: all login: normaluser type: User users_002: created_on: 2015-03-05 19:33:19 +02:00 status: 1 last_login_on: language: en # password = foo salt: 7599f9963ec07b5a3b55b354407120c0 hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed updated_on: 2015-03-05 19:33:19 +02:00 admin: false mail: dlopper@somenet.foo lastname: Lopper firstname: Dave id: 2 auth_source_id: mail_notification: all login: supportclient type: User users_003: id: 3 created_on: 2006-07-19 19:33:19 +02:00 status: 0 last_login_on: language: '' hashed_password: 1 updated_on: 2006-07-19 19:33:19 +02:00 admin: false lastname: Anonymous firstname: '' auth_source_id: mail_notification: only_my_events login: '' type: AnonymousUser ================================================ FILE: test/fixtures/3.0/attachments.yml ================================================ --- attachments_001: created_on: 2006-07-19 21:07:27 +02:00 container_type: Issue container_id: 1 downloads: 0 disk_filename: 060719210727_source.rb disk_directory: "2006/07" digest: b91e08d0cf966d5c6ff411bd8c4cc3a2 id: 1 filesize: 153 filename: source.rb author_id: 1 description: This is a Ruby source file content_type: application/x-ruby ================================================ FILE: test/fixtures/3.0/custom_fields.yml ================================================ --- custom_fields_001: name: owner-email regexp: "" type: IssueCustomField possible_values: "" id: 1 is_required: false field_format: string default_value: "" editable: true visible: true position: 1 custom_fields_002: name: helpdesk-first-reply min_length: 1 regexp: "" visible: false type: ProjectCustomField max_length: 100 possible_values: "" id: 2 is_required: false field_format: text default_value: "" editable: true position: 1 custom_fields_003: name: helpdesk-email-footer min_length: 1 regexp: "" visible: false type: ProjectCustomField max_length: 100 possible_values: "" id: 3 is_required: false field_format: text default_value: "" editable: true position: 1 custom_fields_004: name: helpdesk-sender-email regexp: "" type: ProjectCustomField possible_values: "" id: 4 is_required: false field_format: string default_value: false editable: true visible: true position: 1 custom_fields_005: name: helpdesk-send-to-owner-default regexp: "" type: ProjectCustomField possible_values: "" id: 5 is_required: false field_format: bool default_value: false editable: true visible: false position: 1 custom_fields_006: name: helpdesk-subject min_length: 1 regexp: "" visible: false type: ProjectCustomField max_length: 100 possible_values: "" id: 6 is_required: false field_format: text default_value: "" editable: true position: 1 custom_fields_007: name: helpdesk-send-first-reply visible: false type: ProjectCustomField id: 7 is_required: false field_format: boolean default_value: true editable: true position: 1 custom_fields_008: name: title regexp: "" is_for_all: false type: UserCustomField possible_values: "" id: 8 is_required: false field_format: string default_value: "" editable: true position: 1 custom_fields_009: name: motto regexp: "" is_for_all: false type: UserCustomField possible_values: "" id: 9 is_required: false field_format: string default_value: "" editable: true position: 2 custom_fields_010: name: copy-to regexp: "" type: IssueCustomField possible_values: "" id: 10 is_required: false field_format: string default_value: "" editable: true visible: true position: 1 custom_fields_011: name: cc-handling visible: false type: ProjectCustomField id: 11 is_required: false field_format: bool default_value: false editable: true position: 1 ================================================ FILE: test/fixtures/3.0/custom_fields_projects.yml ================================================ --- custom_fields_projects_001: custom_field_id: 1 project_id: 1 custom_fields_projects_002: custom_field_id: 1 project_id: 2 custom_fields_projects_003: custom_field_id: 10 project_id: 1 custom_fields_projects_004: custom_field_id: 10 project_id: 2 custom_fields_projects_005: custom_field_id: 1 project_id: 4 custom_fields_projects_006: custom_field_id: 10 project_id: 4 ================================================ FILE: test/fixtures/3.0/custom_fields_trackers.yml ================================================ --- custom_fields_trackers_001: custom_field_id: 1 tracker_id: 1 custom_fields_trackers_002: custom_field_id: 1 tracker_id: 2 custom_fields_trackers_003: custom_field_id: 1 tracker_id: 3 custom_fields_trackers_004: custom_field_id: 1 tracker_id: 4 custom_fields_trackers_005: custom_field_id: 10 tracker_id: 1 custom_fields_trackers_006: custom_field_id: 10 tracker_id: 2 custom_fields_trackers_007: custom_field_id: 10 tracker_id: 3 custom_fields_trackers_008: custom_field_id: 10 tracker_id: 4 ================================================ FILE: test/fixtures/3.0/custom_values.yml ================================================ --- custom_values_001: customized_type: Project custom_field_id: 2 customized_id: 1 id: 1 value: "first reply" custom_values_002: customized_type: Project custom_field_id: 3 customized_id: 1 id: 2 value: "email footer" custom_values_003: customized_type: Project custom_field_id: 4 customized_id: 1 id: 3 value: reply@example.com custom_values_004: customized_type: Project custom_field_id: 5 customized_id: 1 id: 4 value: 0 custom_values_005: customized_type: Project custom_field_id: 11 customized_id: 1 id: 5 value: 1 custom_values_006: customized_type: Project custom_field_id: 2 customized_id: 2 id: 6 value: first reply custom_values_007: customized_type: Project custom_field_id: 3 customized_id: 2 id: 7 value: email footer custom_values_008: customized_type: Project custom_field_id: 4 customized_id: 2 id: 8 value: reply@example.com custom_values_009: customized_type: Project custom_field_id: 5 customized_id: 2 id: 9 value: 0 custom_values_010: customized_type: Project custom_field_id: 11 customized_id: 2 id: 10 value: 1 custom_values_011: customized_type: Issue custom_field_id: 1 customized_id: 1 id: 11 value: owner@example.com custom_values_012: customized_type: Project custom_field_id: 2 customized_id: 4 id: 12 value: first reply custom_values_013: customized_type: Project custom_field_id: 3 customized_id: 4 id: 13 value: email footer custom_values_014: customized_type: Project custom_field_id: 4 customized_id: 4 id: 14 value: reply@example.com custom_values_015: customized_type: Project custom_field_id: 5 customized_id: 4 id: 15 value: 0 custom_values_016: customized_type: Project custom_field_id: 11 customized_id: 4 id: 16 value: 0 ================================================ FILE: test/fixtures/3.0/email_addresses.yml ================================================ --- email_address_001: id: 1 user_id: 1 address: rhill@somenet.foo is_default: true created_on: 2006-07-19 19:34:07 +02:00 updated_on: 2006-07-19 19:34:07 +02:00 email_address_002: id: 2 user_id: 2 address: dlopper@somenet.foo is_default: true created_on: 2006-07-19 19:34:07 +02:00 updated_on: 2006-07-19 19:34:07 +02:00 ================================================ FILE: test/fixtures/3.0/enabled_modules.yml ================================================ --- enabled_modules_001: name: issue_tracking project_id: 1 id: 1 enabled_modules_002: name: time_tracking project_id: 1 id: 2 enabled_modules_003: name: files project_id: 1 id: 3 enabled_modules_004: name: issue_tracking project_id: 2 id: 4 enabled_modules_005: name: time_tracking project_id: 2 id: 5 enabled_modules_006: name: files project_id: 2 id: 6 enabled_modules_007: name: issue_tracking project_id: 4 id: 7 enabled_modules_008: name: time_tracking project_id: 4 id: 8 enabled_modules_009: name: files project_id: 4 id: 9 ================================================ FILE: test/fixtures/3.0/enumerations.yml ================================================ --- enumerations_001: name: Low id: 1 type: IssuePriority active: true position: 1 position_name: lowest enumerations_002: name: Normal id: 2 type: IssuePriority is_default: true active: true position: 2 position_name: default enumerations_003: name: High id: 3 type: IssuePriority active: true position: 3 position_name: high3 enumerations_004: name: Urgent id: 4 type: IssuePriority active: true position: 4 position_name: high2 enumerations_005: name: Immediate id: 5 type: IssuePriority active: true position: 5 position_name: highest ================================================ FILE: test/fixtures/3.0/issue_statuses.yml ================================================ --- issue_statuses_001: id: 1 name: New is_closed: false position: 1 issue_statuses_002: id: 2 name: Assigned is_closed: false position: 2 issue_statuses_003: id: 3 name: Resolved is_closed: false position: 3 issue_statuses_004: name: Feedback id: 4 is_closed: false position: 4 issue_statuses_005: id: 5 name: Closed is_closed: true position: 5 issue_statuses_006: id: 6 name: Rejected is_closed: true position: 6 ================================================ FILE: test/fixtures/3.0/issues.yml ================================================ --- issues_001: created_on: 2015-03-05 21:41:21 +02:00 project_id: 1 updated_on: 2015-03-05 22:05:50 +02:00 priority_id: 2 subject: Can't print recipes id: 1 fixed_version_id: category_id: 1 description: Unable to print recipes tracker_id: 1 assigned_to_id: 1 author_id: 1 status_id: 1 start_date: due_date: root_id: 1 lft: 1 rgt: 2 lock_version: 3 issues_002: created_on: 2006-07-19 21:04:21 +02:00 project_id: 2 updated_on: 2006-07-19 21:09:50 +02:00 priority_id: 2 subject: Add ingredients categories id: 2 fixed_version_id: category_id: description: Ingredients of the recipe should be classified by categories tracker_id: 2 assigned_to_id: author_id: 1 status_id: 2 start_date: due_date: root_id: 2 lft: 1 rgt: 2 lock_version: 3 ================================================ FILE: test/fixtures/3.0/journal_details.yml ================================================ --- journal_details_001: old_value: property: attachment id: 1 value: 060719210727_source.rb prop_key: 1 journal_id: 3 ================================================ FILE: test/fixtures/3.0/journals.yml ================================================ --- journals_001: created_on: <%= 2.days.ago.to_date.to_s(:db) %> notes: "Journal notes" id: 1 journalized_type: Issue user_id: 1 journalized_id: 1 journals_002: created_on: <%= 1.days.ago.to_date.to_s(:db) %> notes: "Some notes." id: 2 journalized_type: Issue user_id: 1 journalized_id: 1 journals_003: created_on: <%= 1.days.ago.to_date.to_s(:db) %> notes: "Some notes with an attachment." id: 3 journalized_type: Issue user_id: 1 journalized_id: 1 journals_004: id: 4 created_on: <%= 1.days.ago.to_date.to_s(:db) %> notes: "A comment on an issue." user_id: 1 journalized_type: Issue journalized_id: 2 ================================================ FILE: test/fixtures/3.0/member_roles.yml ================================================ --- member_roles_001: id: 1 role_id: 1 member_id: 1 member_roles_002: id: 2 role_id: 1 member_id: 2 member_roles_003: id: 3 role_id: 1 member_id: 3 member_roles_004: id: 4 role_id: 6 member_id: 4 ================================================ FILE: test/fixtures/3.0/members.yml ================================================ --- members_001: created_on: 2006-07-19 19:35:33 +02:00 project_id: 1 id: 1 user_id: 1 mail_notification: true members_002: created_on: 2006-07-19 19:35:36 +02:00 project_id: 2 id: 2 user_id: 1 mail_notification: true members_003: created_on: 2006-07-19 19:35:36 +02:00 project_id: 3 id: 3 user_id: 1 mail_notification: true members_004: created_on: 2006-07-19 19:35:36 +02:00 project_id: 2 id: 4 user_id: 2 mail_notification: false members_005: created_on: 2006-07-19 19:35:36 +02:00 project_id: 4 id: 5 user_id: 1 mail_notification: true ================================================ FILE: test/fixtures/3.0/projects.yml ================================================ --- projects_001: created_on: 2015-03-05 19:13:59 +02:00 name: Helpdesk Project 1 updated_on: 2015-03-05 22:53:01 +02:00 id: 1 description: First helpdesk project homepage: "" is_public: true identifier: helpdesk_project_1 parent_id: lft: 1 rgt: 2 projects_002: created_on: 2015-03-05 19:14:19 +02:00 name: Helpdesk Project 2 updated_on: 2015-03-05 19:14:19 +02:00 id: 2 description: Second helpdesk project homepage: "" is_public: false identifier: helpdesk_project_2 parent_id: lft: 3 rgt: 4 projects_003: created_on: 2015-03-05 19:15:21 +02:00 name: Irrelevant Project updated_on: 2015-03-05 19:18:12 +02:00 id: 3 description: Project that has no helpdesk features homepage: "" is_public: true identifier: irrelevant_project parent_id: lft: 5 rgt: 6 projects_004: created_on: 2015-03-05 19:14:19 +02:00 name: Helpdesk Project 4 updated_on: 2015-03-05 19:14:19 +02:00 id: 4 description: Disabled copy-to helpdesk project homepage: "" is_public: true identifier: helpdesk_project_4 parent_id: lft: 6 rgt: 7 ================================================ FILE: test/fixtures/3.0/projects_trackers.yml ================================================ --- projects_trackers_001: project_id: 1 tracker_id: 1 projects_trackers_002: project_id: 1 tracker_id: 2 projects_trackers_003: project_id: 1 tracker_id: 3 projects_trackers_004: project_id: 2 tracker_id: 1 projects_trackers_005: project_id: 2 tracker_id: 2 projects_trackers_006: project_id: 2 tracker_id: 3 projects_trackers_007: project_id: 3 tracker_id: 1 projects_trackers_008: project_id: 3 tracker_id: 2 projects_trackers_009: project_id: 3 tracker_id: 3 projects_trackers_010: project_id: 4 tracker_id: 1 projects_trackers_011: project_id: 4 tracker_id: 2 projects_trackers_012: project_id: 4 tracker_id: 3 ================================================ FILE: test/fixtures/3.0/roles.yml ================================================ --- roles_001: name: Manager id: 1 builtin: 0 issues_visibility: all permissions: | --- - :add_project - :edit_project - :close_project - :select_project_modules - :manage_members - :manage_versions - :manage_categories - :view_issues - :add_issues - :edit_issues - :manage_issue_relations - :manage_subtasks - :add_issue_notes - :move_issues - :delete_issues - :view_issue_watchers - :add_issue_watchers - :set_issues_private - :set_notes_private - :view_private_notes - :delete_issue_watchers - :manage_public_queries - :save_queries - :view_gantt - :view_calendar - :log_time - :view_time_entries - :edit_time_entries - :delete_time_entries - :manage_news - :comment_news - :view_documents - :add_documents - :edit_documents - :delete_documents - :view_wiki_pages - :export_wiki_pages - :view_wiki_edits - :edit_wiki_pages - :delete_wiki_pages_attachments - :protect_wiki_pages - :delete_wiki_pages - :rename_wiki_pages - :add_messages - :edit_messages - :delete_messages - :manage_boards - :view_files - :manage_files - :browse_repository - :manage_repository - :view_changesets - :manage_related_issues - :manage_project_activities position: 1 roles_002: name: Developer id: 2 builtin: 0 issues_visibility: default permissions: | --- - :edit_project - :manage_members - :manage_versions - :manage_categories - :view_issues - :add_issues - :edit_issues - :manage_issue_relations - :manage_subtasks - :add_issue_notes - :move_issues - :delete_issues - :view_issue_watchers - :save_queries - :view_gantt - :view_calendar - :log_time - :view_time_entries - :edit_own_time_entries - :manage_news - :comment_news - :view_documents - :add_documents - :edit_documents - :delete_documents - :view_wiki_pages - :view_wiki_edits - :edit_wiki_pages - :protect_wiki_pages - :delete_wiki_pages - :add_messages - :edit_own_messages - :delete_own_messages - :manage_boards - :view_files - :manage_files - :browse_repository - :view_changesets position: 2 roles_003: name: Reporter id: 3 builtin: 0 issues_visibility: default permissions: | --- - :edit_project - :manage_members - :manage_versions - :manage_categories - :view_issues - :add_issues - :edit_issues - :manage_issue_relations - :add_issue_notes - :move_issues - :view_issue_watchers - :save_queries - :view_gantt - :view_calendar - :log_time - :view_time_entries - :manage_news - :comment_news - :view_documents - :add_documents - :edit_documents - :delete_documents - :view_wiki_pages - :view_wiki_edits - :edit_wiki_pages - :delete_wiki_pages - :add_messages - :manage_boards - :view_files - :manage_files - :browse_repository - :view_changesets position: 3 roles_004: name: Non member id: 4 builtin: 1 issues_visibility: default permissions: | --- - :view_issues - :add_issues - :edit_issues - :manage_issue_relations - :add_issue_notes - :save_queries - :view_gantt - :view_calendar - :log_time - :view_time_entries - :comment_news - :view_documents - :view_wiki_pages - :view_wiki_edits - :edit_wiki_pages - :add_messages - :view_files - :manage_files - :browse_repository - :view_changesets position: 4 roles_005: name: Anonymous id: 5 builtin: 2 issues_visibility: default permissions: | --- - :treat_user_as_supportclient - :view_issues - :add_issue_notes - :view_gantt - :view_calendar - :view_time_entries - :view_documents - :view_wiki_pages - :view_wiki_edits - :view_files - :browse_repository - :view_changesets position: 5 roles_006: name: SupportClient id: 6 builtin: 0 issues_visibility: default permissions: | --- - :treat_user_as_supportclient - :add_issues - :add_issue_notes position: 6 ================================================ FILE: test/fixtures/3.0/trackers.yml ================================================ --- trackers_001: name: Bug id: 1 is_in_chlog: true position: 1 default_status_id: 1 trackers_002: name: Feature request id: 2 is_in_chlog: true position: 2 default_status_id: 1 trackers_003: name: Support request id: 3 is_in_chlog: false position: 3 default_status_id: 1 ================================================ FILE: test/fixtures/3.0/users.yml ================================================ --- users_001: created_on: 2015-03-05 19:34:07 +02:00 status: 1 last_login_on: language: en # password = foo salt: 3126f764c3c5ac61cbfc103f25f934cf hashed_password: 9e4dd7eeb172c12a0691a6d9d3a269f7e9fe671b updated_on: 2015-03-05 19:34:07 +02:00 admin: true lastname: Hill firstname: Robert id: 1 auth_source_id: mail_notification: all login: normaluser type: User users_002: created_on: 2015-03-05 19:33:19 +02:00 status: 1 last_login_on: language: en # password = foo salt: 7599f9963ec07b5a3b55b354407120c0 hashed_password: 8f659c8d7c072f189374edacfa90d6abbc26d8ed updated_on: 2015-03-05 19:33:19 +02:00 admin: false lastname: Lopper firstname: Dave id: 2 auth_source_id: mail_notification: all login: supportclient type: User users_003: id: 3 created_on: 2006-07-19 19:33:19 +02:00 status: 0 last_login_on: language: '' hashed_password: 1 updated_on: 2006-07-19 19:33:19 +02:00 admin: false lastname: Anonymous firstname: '' auth_source_id: mail_notification: only_my_events login: '' type: AnonymousUser ================================================ FILE: test/fixtures/files/2006/07/060719210727_source.rb ================================================ # The Greeter class class Greeter def initialize(name) @name = name.capitalize end def salute puts "Hello #{@name}!" end end ================================================ FILE: test/fixtures/mail_handler/ticket_by_unknown_user.eml ================================================ Return-Path: Received: from osiris ([127.0.0.1]) by OSIRIS with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris> From: "John Doe" To: Subject: Ticket by unknown user Date: Sun, 22 Jun 2008 12:28:07 +0200 MIME-Version: 1.0 Content-Type: text/plain; format=flowed; charset="iso-8859-1"; reply-type=original Content-Transfer-Encoding: 7bit This is a ticket submitted by an unknown user. ================================================ FILE: test/fixtures/mail_handler/ticket_by_unknown_user_with_cc.eml ================================================ Return-Path: Received: from osiris ([127.0.0.1]) by OSIRIS with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris> From: "John Doe" To: Cc: "Ada" , My Group: , Bob ; Subject: Ticket by unknown user Date: Sun, 22 Jun 2008 12:28:07 +0200 MIME-Version: 1.0 Content-Type: text/plain; format=flowed; charset="iso-8859-1"; reply-type=original Content-Transfer-Encoding: 7bit This is a ticket submitted by an unknown user with cc. ================================================ FILE: test/fixtures/mail_handler/ticket_by_user_1.eml ================================================ Return-Path: Received: from osiris ([127.0.0.1]) by OSIRIS with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris> From: "John Doe" To: Subject: Ticket by unknown user Date: Sun, 22 Jun 2008 12:28:07 +0200 MIME-Version: 1.0 Content-Type: text/plain; format=flowed; charset="iso-8859-1"; reply-type=original Content-Transfer-Encoding: 7bit This is a ticket submitted by an unknown user. ================================================ FILE: test/fixtures/mail_handler/ticket_by_user_2.eml ================================================ Return-Path: Received: from osiris ([127.0.0.1]) by OSIRIS with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris> From: "John Doe" To: Subject: Ticket by unknown user Date: Sun, 22 Jun 2008 12:28:07 +0200 MIME-Version: 1.0 Content-Type: text/plain; format=flowed; charset="iso-8859-1"; reply-type=original Content-Transfer-Encoding: 7bit This is a ticket submitted by an unknown user. ================================================ FILE: test/fixtures/mail_handler/ticket_reply.eml ================================================ Return-Path: Received: from osiris ([127.0.0.1]) by OSIRIS with hMailServer ; Sat, 21 Jun 2008 18:41:39 +0200 Message-ID: <006a01c8d3bd$ad9baec0$0a00a8c0@osiris> In-Reply-To: From: "John Smith" To: References: <485d0ad366c88_d7014663a025f@osiris.tmail> Subject: Re: Add ingredients categories Date: Sat, 21 Jun 2008 18:41:39 +0200 MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_NextPart_000_0067_01C8D3CE.711F9CC0" X-Priority: 3 X-MSMail-Priority: Normal X-Mailer: Microsoft Outlook Express 6.00.2900.2869 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869 This is a multi-part message in MIME format. ------=_NextPart_000_0067_01C8D3CE.711F9CC0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This is reply ------=_NextPart_000_0067_01C8D3CE.711F9CC0 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: quoted-printable =EF=BB=BF
This is=20 reply
------=_NextPart_000_0067_01C8D3CE.711F9CC0-- ================================================ FILE: test/fixtures/mail_handler/ticket_with_attachment.eml ================================================ Return-Path: Received: from osiris ([127.0.0.1]) by OSIRIS with hMailServer ; Sat, 21 Jun 2008 15:53:25 +0200 Message-ID: <002301c8d3a6$2cdf6950$0a00a8c0@osiris> From: "John Smith" To: Subject: Ticket created by email with attachment Date: Sat, 21 Jun 2008 15:53:25 +0200 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_001F_01C8D3B6.F05C5270" X-Priority: 3 X-MSMail-Priority: Normal X-Mailer: Microsoft Outlook Express 6.00.2900.2869 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869 This is a multi-part message in MIME format. ------=_NextPart_000_001F_01C8D3B6.F05C5270 Content-Type: multipart/alternative; boundary="----=_NextPart_001_0020_01C8D3B6.F05C5270" ------=_NextPart_001_0020_01C8D3B6.F05C5270 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable This is a new ticket with attachments ------=_NextPart_001_0020_01C8D3B6.F05C5270 Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable
This is  a new ticket with=20 attachments
------=_NextPart_001_0020_01C8D3B6.F05C5270-- ------=_NextPart_000_001F_01C8D3B6.F05C5270 Content-Type: image/jpeg; name="Paella.jpg" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="Paella.jpg" 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 cbis+/WpUqUcMZKdF44n/9k= ------=_NextPart_000_001F_01C8D3B6.F05C5270-- ================================================ FILE: test/fixtures/mail_handler/ticket_with_attributes.eml ================================================ Return-Path: Received: from osiris ([127.0.0.1]) by OSIRIS with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris> From: "John Smith" To: Subject: New ticket on a given project Date: Sun, 22 Jun 2008 12:28:07 +0200 MIME-Version: 1.0 Content-Type: text/plain; format=flowed; charset="iso-8859-1"; reply-type=original Content-Transfer-Encoding: 7bit X-Priority: 3 X-MSMail-Priority: Normal X-Mailer: Microsoft Outlook Express 6.00.2900.2869 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2869 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas imperdiet turpis et odio. Integer eget pede vel dolor euismod varius. Phasellus blandit eleifend augue. Nulla facilisi. Duis id diam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In in urna sed tellus aliquet lobortis. Morbi scelerisque tortor in dolor. Cras sagittis odio eu lacus. Aliquam sem tortor, consequat sit amet, vestibulum id, iaculis at, lectus. Fusce tortor libero, congue ut, euismod nec, luctus eget, eros. Pellentesque tortor enim, feugiat in, dignissim eget, tristique sed, mauris. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque sit amet libero. In hac habitasse platea dictumst. Nulla et nunc. Duis pede. Donec et ipsum. Nam ut dui tincidunt neque sollicitudin iaculis. Duis vitae dolor. Vestibulum eget massa. Sed lorem. Nullam volutpat cursus erat. Cras felis dolor, lacinia quis, rutrum et, dictum et, ligula. Sed erat nibh, gravida in, accumsan non, placerat sed, massa. Sed sodales, ante fermentum ultricies sollicitudin, massa leo pulvinar dui, a gravida orci mi eget odio. Nunc a lacus. Project: onlinestore Tracker: Feature Request category: stock management priority: URGENT ================================================ FILE: test/functional/issues_controller_with_helpdesk_test.rb ================================================ require File.dirname(__FILE__) + '/../test_helper' class IssuesControllerWithHelpdeskTest < ActionController::TestCase include Redmine::I18n fixtures :all def setup User.current = nil @controller = IssuesController.new end def test_render_send_to_owner_checkbox issue = Issue.find(1) @request.session[:user_id] = 1 get :edit, :id => 1 assert_response :success assert_select "#send_to_owner", 1 end test "send_to_owner not renderer without owner-email" do issue = Issue.find(1) owner_field = CustomField.find_by_name('owner-email') owner_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.id, owner_field.id). first owner_value.value = "" owner_value.save! @request.session[:user_id] = 1 get :edit, :id => 1 assert_response :success assert_select "#send_to_owner", 0 end test "send_to_owner checked if send-to-owner-default is set to yes" do issue = Issue.find(1) default_field = CustomField.find_by_name('helpdesk-send-to-owner-default') default_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, default_field.id). first default_value.value = "1" default_value.save! @request.session[:user_id] = 1 get :edit, :id => 1 assert_response :success assert_select "#send_to_owner", 1 assert_equal "checked", css_select("#send_to_owner").first["checked"] end test "send_to_owner checked if send-to-owner-default is set to no" do issue = Issue.find(1) default_field = CustomField.find_by_name('helpdesk-send-to-owner-default') default_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, default_field.id). first default_value.value = "2" default_value.save! @request.session[:user_id] = 1 get :edit, :id => 1 assert_response :success assert_select "#send_to_owner", 1 assert_not_equal "checked", css_select("#send_to_owner").first["checked"] end test "history note added to journal if note has been sent to owner" do issue = Issue.find(1) journal = issue.journals.first journal.send_to_owner = true journal.save @request.session[:user_id] = 1 get :show, :id => 1 assert_response :success assert_select "#history>i", 1 assert_select "#history>i", "This answer was sent to the supportclient." end test "history note not added to journal if note has not been sent to owner" do issue = Issue.find(1) journal = issue.journals.first journal.send_to_owner = false journal.save @request.session[:user_id] = 1 get :show, :id => 1 assert_response :success assert_select "#history>i", 0 end test "send_to_owner checked" do ActionMailer::Base.delivery_method = :test ActionMailer::Base.deliveries.clear @request.session[:user_id] = 1 assert_difference('Journal.count') do put :update, :id => 1, :issue => {:assigned_to_id => 1, :notes => 'Assigned'}, :send_to_owner => "true" end assert_redirected_to :action => 'show', :id => '1' assert Issue.find(1).journals.last.send_to_owner mail = ActionMailer::Base.deliveries.last assert_equal "owner@example.com", mail.to.first assert_equal "Assigned\n\nemail footer", mail.body.to_s end test "send_to_owner not checked" do ActionMailer::Base.delivery_method = :test ActionMailer::Base.deliveries.clear @request.session[:user_id] = 1 assert_difference('Journal.count') do put :update, :id => 1, :issue => {:assigned_to_id => 1, :notes => 'Assigned'}, :send_to_owner => "false" end assert_redirected_to :action => 'show', :id => '1' assert !Issue.find(1).journals.last.send_to_owner ActionMailer::Base.deliveries.each do |mail| assert !mail.to.include?("owner@example.com") end end end ================================================ FILE: test/test_helper.rb ================================================ require "simplecov" require "codeclimate-test-reporter" SimpleCov.use_merging true SimpleCov.merge_timeout 3600 SimpleCov.add_filter '/test/' SimpleCov.add_filter 'init.rb' SimpleCov.formatters = [] SimpleCov.start(CodeClimate::TestReporter.configuration.profile) do root File.expand_path(File.dirname(__FILE__) + '/../../') end # Load the normal Rails helper require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper') class TestHelper def self.files_path File.dirname(__FILE__) + '/fixtures/' end def self.fixture_path if Redmine::VERSION::MAJOR >= 3 File.dirname(__FILE__) + '/fixtures/3.0' else File.dirname(__FILE__) + '/fixtures/2.6' end end end class ActiveSupport::TestCase self.fixture_path = TestHelper.fixture_path end ================================================ FILE: test/unit/helpdesk_mailer_test.rb ================================================ require File.dirname(__FILE__) + '/../test_helper' class HelpdeskMailerTest < ActionMailer::TestCase include Redmine::I18n self.use_transactional_tests = true fixtures :all def setup ActionMailer::Base.deliveries.clear Setting.host_name = 'mydomain.foo' Setting.protocol = 'http' Setting.plain_text_mail = '0' Setting.default_language = 'en' User.current = nil end def teardown Setting.clear_cache end def test_default_url_options default_url_object = HelpdeskMailer.default_url_options assert_equal "mydomain.foo", default_url_object[:host] assert_equal "http", default_url_object[:protocol] end def test_email_headers issue = Issue.find(1) email = HelpdeskMailer. email_to_supportclient(issue, {:recipient => "owner@example.com"}). deliver assert !ActionMailer::Base.deliveries.empty? assert_not_nil email assert_equal issue.project.identifier, email.header['X-Redmine-Project'].to_s assert_equal issue.id, email.header['X-Redmine-Issue-Id'].to_s.to_i assert_equal issue.author.login, email.header['X-Redmine-Issue-Author'].to_s assert_equal issue.assigned_to.login, email.header['X-Redmine-Issue-Assignee'].to_s assert_equal issue.tracker, email.header['X-Redmine-Issue-Tracker'].to_s #assert_equal 'OOF', mail.header['X-Auto-Response-Suppress'].to_s #assert_equal 'auto-generated', mail.header['Auto-Submitted'].to_s #assert_equal '', mail.header['List-Id'].to_s end def test_email_default_sender issue = Issue.find(1) s = CustomField.find_by_name('helpdesk-sender-email') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, s.id). first custom_value.destroy email = HelpdeskMailer. email_to_supportclient(issue, {:recipient => "owner@example.com"}). deliver assert !ActionMailer::Base.deliveries.empty? assert_equal Setting.mail_from, email.header['From'].to_s end def test_email_helpdesk_sender issue = Issue.find(1) email = HelpdeskMailer. email_to_supportclient(issue, {:recipient => "owner@example.com"}). deliver assert !ActionMailer::Base.deliveries.empty? assert_equal "reply@example.com", email.header['From'].to_s end def test_email_helpdesk_sender_with_phrase issue = Issue.find(1) s = CustomField.find_by_name('helpdesk-sender-email') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, s.id). first custom_value.value = "Redmine helpdesk " custom_value.save email = HelpdeskMailer. email_to_supportclient(issue, {:recipient => "owner@example.com"}).deliver assert !ActionMailer::Base.deliveries.empty? assert_equal "Redmine helpdesk ", email.header['From'].to_s end def test_first_reply issue = Issue.find(1) email = HelpdeskMailer. email_to_supportclient(issue, {:recipient => "owner@example.com"}). deliver assert !ActionMailer::Base.deliveries.empty? assert_match /^redmine\.issue-1\.\d+\.[a-f0-9]+@example\.net/, email.message_id assert_match /redmine\.issue-1\.\d+@example\.net/, email.references assert_equal "first reply", email.body.to_s end def test_edit issue = Issue.find(1) email = HelpdeskMailer. email_to_supportclient( issue, {:recipient => "owner@example.com", :journal => Journal.find(1), :text => 'text' }). deliver assert !ActionMailer::Base.deliveries.empty? assert_match /^redmine\.issue-1\.\d+\.[a-f0-9]+@example\.net/, email.message_id assert_match /redmine\.issue-1\.\d+@example\.net/, email.references assert_equal "text\n\nemail footer", email.body.to_s end def test_fallback_message_id issue = Issue.find(2) s = CustomField.find_by_name('helpdesk-first-reply') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, s.id). first custom_value.destroy email = HelpdeskMailer. email_to_supportclient( issue, {:recipient => "owner@example.com", :journal => Journal.find(1), :text => 'text' }). deliver assert !ActionMailer::Base.deliveries.empty? assert_match /^redmine\.issue-2\.\d+\.[a-f0-9]+@example\.net/, email.message_id assert_match /redmine\.issue-2\.\d+@example\.net/, email.references end def test_subject issue = Issue.find(1) email = HelpdeskMailer. email_to_supportclient(issue, {:recipient => "owner_email"}). deliver assert !ActionMailer::Base.deliveries.empty? assert_equal "[#{issue.project.name} - ##{issue.id}] #{issue.subject}", email.subject.to_s end # Test with single attachment and verify against fixture file def test_attachments_added Attachment.storage_path = TestHelper.files_path + '/files' issue = Issue.find(1) email = HelpdeskMailer. email_to_supportclient( issue, {:recipient => "owner@example.com", :journal => Journal.find(3), :text => 'text' }).deliver assert !ActionMailer::Base.deliveries.empty? mail = ActionMailer::Base.deliveries.last assert_not_nil mail attachments_length = mail.attachments.length assert_equal 1, attachments_length filename = mail.attachments[0].filename assert_equal "source.rb", filename content = mail.attachments[0].body.to_s original_content = File.open( Attachment.find( Journal.find(3).details.first.prop_key).diskfile).read assert_equal original_content, content end end ================================================ FILE: test/unit/journal_patch_test.rb ================================================ require File.dirname(__FILE__) + '/../test_helper' class JournalPatchTest < ActiveSupport::TestCase include Redmine::I18n self.use_transactional_tests = true fixtures :all def setup User.current = User.find(1) end def teardown Setting.clear_cache end def test_notification_not_sent_when_send_to_owner_false HelpdeskMailer.any_instance.expects(:email_to_supportclient).never Mailer.any_instance.stubs(:deliver_issue_edit).returns(true) issue = Issue.find(1) journal = issue.journals.first journal.send_to_owner = false journal.save! journal.send(:send_notification) end def test_notification_not_sent_when_notes_length_zero HelpdeskMailer.any_instance.expects(:email_to_supportclient).never Mailer.any_instance.stubs(:deliver_issue_edit).returns(true) issue = Issue.find(1) journal = issue.journals.first journal.notes = "" journal.send_to_owner = true journal.save! journal.send(:send_notification) end def test_notification_note_sent_when_owner_email_blank HelpdeskMailer.any_instance.expects(:email_to_supportclient).never Mailer.any_instance.stubs(:deliver_issue_edit).returns(true) issue = Issue.find(1) journal = issue.journals.first owner_field = CustomField.find_by_name('owner-email') owner_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.id, owner_field.id). first owner_value.value = "" owner_value.save! journal.send_to_owner = true journal.save! journal.send(:send_notification) end def test_notification_sent Mailer.any_instance.stubs(:deliver_issue_edit).returns(true) issue = Issue.find(1) journal = issue.journals.first journal.send_to_owner = true journal.save! HelpdeskMailer.any_instance.expects(:email_to_supportclient).with(issue, {:recipient => "owner@example.com", :journal => journal, :text => journal.notes }).once journal.send(:send_notification) end end ================================================ FILE: test/unit/macro_expander_test.rb ================================================ require File.dirname(__FILE__) + '/../test_helper' class MacroExpanderTest < ActionMailer::TestCase include Redmine::I18n self.use_transactional_tests = true fixtures :all def setup ActionMailer::Base.deliveries.clear Setting.host_name = 'mydomain.foo' Setting.protocol = 'http' Setting.plain_text_mail = '0' Setting.default_language = 'en' User.current = nil end def teardown Setting.clear_cache end def test_expand_issue issue = Issue.find(1) s = CustomField.find_by_name('helpdesk-email-footer') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, s.id). first custom_value.value = "##issue-id##, ##issue-subject##, ##issue-tracker##, ##issue-status##" custom_value.save email = HelpdeskMailer. email_to_supportclient( issue, { :recipient => "owner@example.com", :journal => Journal.find(1), :text => 'text' }).deliver assert !ActionMailer::Base.deliveries.empty? assert "text\n\n#{issue.id}, #{issue.subject}, #{issue.tracker}, #{issue.status}", email.body.to_s end def test_expand_project issue = Issue.find(1) s = CustomField.find_by_name('helpdesk-email-footer') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, s.id). first custom_value.value = "##project-name##" custom_value.save email = HelpdeskMailer. email_to_supportclient( issue, { :recipient => "owner@example.com", :journal => Journal.find(1), :text =>'text' }).deliver assert !ActionMailer::Base.deliveries.empty? assert "text\n\n#{issue.project.name}", email.body.to_s end def test_expand_user issue = Issue.find(1) s = CustomField.find_by_name('helpdesk-email-footer') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, s.id). first custom_value.value = "##user-name##, ##user-firstname##, ##user-lastname##, ##user-mail##, ##user-login##" custom_value.save email = HelpdeskMailer. email_to_supportclient( issue, { :recipient => "owner@example.com", :journal => Journal.find(1), :text => 'text' }).deliver assert !ActionMailer::Base.deliveries.empty? user = Journal.find(1).user assert "text\n\n#{user.name}, #{user.firstname}, #{user.lastname}, #{user.mail}, #{user.login}", email.body.to_s end def test_expand_user_cfs_w_not_existing issue = Issue.find(1) user = Journal.find(1).user s = CustomField.find_by_name('helpdesk-email-footer') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, s.id). first custom_value.value = "##user-cf-title##, ##user-cf-motto##, ##user-cf-invalid##." custom_value.save title = CustomField.find_by_name('title') custom_value = CustomValue.new( :customized_id => user.id, :custom_field_id => title.id, :value => "junior senior" ) custom_value.save motto = CustomField.find_by_name('motto') custom_value = CustomValue.new( :customized_id => user.id, :custom_field_id => motto.id, :value => "epic motto" ) custom_value.save email = HelpdeskMailer. email_to_supportclient( issue, {:recipient => "owner@example.com", :journal => Journal.find(1), :text =>'text' }).deliver assert !ActionMailer::Base.deliveries.empty? assert "text\n\njunior senior, epic motto, .", email.body.to_s end def test_expand_user_no_cfs issue = Issue.find(1) user = Journal.find(1).user s = CustomField.find_by_name('helpdesk-email-footer') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, s.id). first custom_value.value = "##user-cf-title##, ##user-cf-motto##, ##user-cf-invalid##." custom_value.save email = HelpdeskMailer. email_to_supportclient( issue, { :recipient => "owner@example.com", :journal => Journal.find(1), :text => 'text' }).deliver assert !ActionMailer::Base.deliveries.empty? assert "text\n\n, , .", email.body.to_s end def test_expand_base issue = Issue.find(1) s = CustomField.find_by_name('helpdesk-email-footer') custom_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.project.id, s.id). first custom_value.value = "##time-now##" custom_value.save t1 = I18n.l(Time.zone.now) email = HelpdeskMailer. email_to_supportclient( issue, { :recipient => "owner@example.com", :journal => Journal.find(1), :text => 'text' }).deliver assert !ActionMailer::Base.deliveries.empty? assert "text\n\n#{t1}", email.body.to_s end end ================================================ FILE: test/unit/mail_handler_patch_test.rb ================================================ require File.dirname(__FILE__) + '/../test_helper' class MailHandlerPatchTest < ActiveSupport::TestCase include Redmine::I18n self.use_transactional_tests = true fixtures :all def setup ActionMailer::Base.deliveries.clear Setting.notified_events = Redmine::Notifiable.all.collect(&:name) end def teardown Setting.clear_cache end def test_helpdesk_dispatch_not_supportclient HelpdeskMailer.any_instance.expects(:email_to_supportclient).never issue = submit_email('ticket_by_user_1.eml', :issue => {:project => 'helpdesk_project_1'}, :unknown_user => 'accept', :no_permission_check => 1) assert_issue_created issue owner_field = CustomField.find_by_name('owner-email') owner_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.id, owner_field.id). first assert owner_value.value.blank? assert User.find(1).login, issue.author.login end def test_helpdesk_dispatch_disabled_copy_to assert_no_difference 'User.count' do HelpdeskMailer.any_instance.expects(:email_to_supportclient).with( kind_of(Issue), {:recipient =>"john.doe@somenet.foo", :carbon_copy => nil} ).once issue = submit_email('ticket_by_unknown_user_with_cc.eml', :issue => {:project => 'helpdesk_project_4'}, :unknown_user => 'accept', :no_permission_check => 1) assert_issue_created issue owner_field = CustomField.find_by_name('owner-email') owner_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.id, owner_field.id). first assert_equal "john.doe@somenet.foo", owner_value.value assert issue.author.anonymous? end end def test_helpdesk_dispatch_anonymous_as_supportclient assert_no_difference 'User.count' do HelpdeskMailer.any_instance.expects(:email_to_supportclient).with( kind_of(Issue), {:recipient =>"john.doe@somenet.foo", :carbon_copy => nil}).once issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'helpdesk_project_1'}, :unknown_user => 'accept', :no_permission_check => 1) assert_issue_created issue owner_field = CustomField.find_by_name('owner-email') owner_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.id, owner_field.id). first assert_equal "john.doe@somenet.foo", owner_value.value assert issue.author.anonymous? end end def test_helpdesk_dispatch_anonymous_as_supportclient_with_cc assert_no_difference 'User.count' do HelpdeskMailer.any_instance.expects(:email_to_supportclient).with( kind_of(Issue), {:recipient =>"john.doe@somenet.foo", :carbon_copy => "Ada , mikel@test.lindsaar.net, Bob "} ).once issue = submit_email('ticket_by_unknown_user_with_cc.eml', :issue => {:project => 'helpdesk_project_1'}, :unknown_user => 'accept', :no_permission_check => 1) assert_issue_created issue owner_field = CustomField.find_by_name('owner-email') owner_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.id, owner_field.id). first copy_to_field = CustomField.find_by_name('copy-to') copy_to_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.id, copy_to_field.id). first assert_equal "john.doe@somenet.foo", owner_value.value assert issue.author.anonymous? assert_equal "Ada , mikel@test.lindsaar.net, Bob ", copy_to_value.value end end def test_helpdesk_dispatch_supportclient HelpdeskMailer.any_instance.expects(:email_to_supportclient).with(kind_of(Issue), {:recipient => User.find(2).mail, :carbon_copy => nil}) issue = submit_email('ticket_by_user_2.eml', :issue => {:project => 'helpdesk_project_2'}, :unknown_user => 'accept', :no_permission_check => 1) assert_issue_created issue owner_field = CustomField.find_by_name('owner-email') owner_value = CustomValue.where( "customized_id = ? AND custom_field_id = ?", issue.id, owner_field.id). first assert_equal User.find(2).mail, owner_value.value assert User.find(2).login, issue.author.login end # TODO: Attachments def submit_email(filename, options={}) raw = IO.read(File.join(TestHelper.files_path, 'mail_handler', filename)) yield raw if block_given? MailHandler.receive(raw, options) end def assert_issue_created(issue) assert issue.is_a?(Issue) assert !issue.new_record? issue.reload end end