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

## 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

## 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
================================================
<i style="display:block;margin-bottom:10px;"><%=h t('email_was_send_to_supportclient') %></i>
================================================
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"
"<pre>\n" + Mail::Encodings.unquote_and_convert_to(details, 'utf-8') + "</pre>\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: <john.doe@somenet.foo>
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" <john.doe@somenet.foo>
To: <redmine@somenet.foo>
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: <john.doe@somenet.foo>
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" <john.doe@somenet.foo>
To: <redmine@somenet.foo>
Cc: "Ada" <ada@test.lindsaar.net>, My Group: <mikel@test.lindsaar.net>, Bob <bob@test.lindsaar.net>;
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: <rhill@somenet.foo>
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" <rhill@somenet.foo>
To: <redmine@somenet.foo>
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: <dlopper@somenet.foo>
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" <dlopper@somenet.foo>
To: <redmine@somenet.foo>
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: <jsmith@somenet.foo>
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: <redmine.issue-2.20060719210421@osiris>
From: "John Smith" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
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<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; charset=3Dutf-8">
<STYLE>BODY {
FONT-SIZE: 0.8em; COLOR: #484848; FONT-FAMILY: Verdana, sans-serif
}
BODY H1 {
FONT-SIZE: 1.2em; MARGIN: 0px; FONT-FAMILY: "Trebuchet MS", Verdana, =
sans-serif
}
A {
COLOR: #2a5685
}
A:link {
COLOR: #2a5685
}
A:visited {
COLOR: #2a5685
}
A:hover {
COLOR: #c61a1a
}
A:active {
COLOR: #c61a1a
}
HR {
BORDER-RIGHT: 0px; BORDER-TOP: 0px; BACKGROUND: #ccc; BORDER-LEFT: 0px; =
WIDTH: 100%; BORDER-BOTTOM: 0px; HEIGHT: 1px
}
.footer {
FONT-SIZE: 0.8em; FONT-STYLE: italic
}
</STYLE>
<META content=3D"MSHTML 6.00.2900.2883" name=3DGENERATOR></HEAD>
<BODY bgColor=3D#ffffff>
<DIV><SPAN class=3Dfooter><FONT face=3DArial color=3D#000000 =
size=3D2>This is=20
reply</FONT></DIV></SPAN></BODY></HTML>
------=_NextPart_000_0067_01C8D3CE.711F9CC0--
================================================
FILE: test/fixtures/mail_handler/ticket_with_attachment.eml
================================================
Return-Path: <jsmith@somenet.foo>
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" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.2900.2883" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>This is a new ticket with=20
attachments</FONT></DIV></BODY></HTML>
------=_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: <jsmith@somenet.foo>
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" <jsmith@somenet.foo>
To: <redmine@somenet.foo>
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 '<redmine.example.net>', 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 <reply@example.com>"
custom_value.save
email = HelpdeskMailer.
email_to_supportclient(issue, {:recipient => "owner@example.com"}).deliver
assert !ActionMailer::Base.deliveries.empty?
assert_equal "Redmine helpdesk <reply@example.com>", 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 <ada@test.lindsaar.net>, mikel@test.lindsaar.net, Bob <bob@test.lindsaar.net>"}
).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 <ada@test.lindsaar.net>, mikel@test.lindsaar.net, Bob <bob@test.lindsaar.net>",
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
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
SYMBOL INDEX (124 symbols across 24 files)
FILE: db/migrate/001_create_custom_owner_email_field.rb
class CreateCustomOwnerEmailField (line 1) | class CreateCustomOwnerEmailField < ActiveRecord::Migration[5.2]
method up (line 2) | def self.up
method down (line 19) | def self.down
FILE: db/migrate/002_create_custom_fields_for_reply.rb
class CreateCustomFieldsForReply (line 1) | class CreateCustomFieldsForReply < ActiveRecord::Migration[5.2]
method up (line 2) | def self.up
method down (line 19) | def self.down
FILE: db/migrate/003_create_custom_field_for_sender_email.rb
class CreateCustomFieldForSenderEmail (line 1) | class CreateCustomFieldForSenderEmail < ActiveRecord::Migration[5.2]
method up (line 2) | def self.up
method down (line 12) | def self.down
FILE: db/migrate/004_create_custom_field_for_send_to_owner_default.rb
class CreateCustomFieldForSendToOwnerDefault (line 1) | class CreateCustomFieldForSendToOwnerDefault < ActiveRecord::Migration[5.2]
method up (line 2) | def self.up
method down (line 12) | def self.down
FILE: db/migrate/005_add_treat_as_supportclient_to_anonymous.rb
class AddTreatAsSupportclientToAnonymous (line 1) | class AddTreatAsSupportclientToAnonymous < ActiveRecord::Migration[5.2]
method up (line 2) | def self.up
method down (line 10) | def self.down
FILE: db/migrate/006_append_footer_to_first_reply.rb
class AppendFooterToFirstReply (line 1) | class AppendFooterToFirstReply < ActiveRecord::Migration[5.2]
method up (line 5) | def self.up
FILE: db/migrate/007_create_custom_copy_to_field.rb
class CreateCustomCopyToField (line 1) | class CreateCustomCopyToField < ActiveRecord::Migration[5.2]
method up (line 2) | def self.up
method down (line 14) | def self.down
FILE: db/migrate/008_create_custom_field_cc_handling.rb
class CreateCustomFieldCcHandling (line 1) | class CreateCustomFieldCcHandling < ActiveRecord::Migration[5.2]
method up (line 2) | def self.up
method down (line 12) | def self.down
FILE: db/migrate/009_create_custom_field_for_reopen_closed_issues_by_email.rb
class CreateCustomFieldForReopenClosedIssuesByEmail (line 1) | class CreateCustomFieldForReopenClosedIssuesByEmail < ActiveRecord::Migr...
method up (line 2) | def self.up
method down (line 12) | def self.down
FILE: db/migrate/010_create_custom_field_for_reply_separator.rb
class CreateCustomFieldForReplySeparator (line 1) | class CreateCustomFieldForReplySeparator < ActiveRecord::Migration[5.2]
method up (line 2) | def self.up
method down (line 17) | def self.down
FILE: lib/helpdesk_hooks.rb
class HelpdeskHooks (line 1) | class HelpdeskHooks < Redmine::Hook::Listener
method view_issues_edit_notes_bottom (line 4) | def view_issues_edit_notes_bottom(context={})
method controller_issues_edit_before_save (line 26) | def controller_issues_edit_before_save(context={})
method view_issues_history_journal_bottom (line 32) | def view_issues_history_journal_bottom(context={})
FILE: lib/helpdesk_mailer.rb
class HelpdeskMailer (line 6) | class HelpdeskMailer < ActionMailer::Base
method default_url_options (line 13) | def self.default_url_options
method email_to_supportclient (line 18) | def email_to_supportclient(issue, params)
method redmine_headers (line 115) | def redmine_headers(h)
method token_for (line 119) | def self.token_for(object, rand=true)
method message_id_for (line 135) | def self.message_id_for(object)
method references_for (line 141) | def self.references_for(object)
method message_id (line 145) | def message_id(object)
method references (line 149) | def references(object)
FILE: lib/macro_expander.rb
type MacroExpander (line 1) | module MacroExpander
function expand_macros (line 2) | def expand_macros(string, issue, journal)
class Expander (line 7) | class Expander
method initialize (line 10) | def initialize(string, issue, journal)
method expand (line 16) | def expand
method expand_issue (line 29) | def expand_issue
method expand_project (line 36) | def expand_project
method expand_user (line 41) | def expand_user
method expand_user_cf (line 51) | def expand_user_cf(user)
method expand_base (line 60) | def expand_base
FILE: lib/redmine_helpdesk/journal_patch.rb
type RedmineHelpdesk (line 1) | module RedmineHelpdesk
type JournalPatch (line 2) | module JournalPatch
function included (line 3) | def self.included(base) # :nodoc:
type InstanceMethods (line 12) | module InstanceMethods
function send_notification_with_helpdesk (line 15) | def send_notification_with_helpdesk
FILE: lib/redmine_helpdesk/mail_handler_patch.rb
type RedmineHelpdesk (line 1) | module RedmineHelpdesk
type MailHandlerPatch (line 2) | module MailHandlerPatch
function included (line 3) | def self.included(base) # :nodoc:
type InstanceMethods (line 15) | module InstanceMethods
function dispatch_to_default_with_helpdesk (line 20) | def dispatch_to_default_with_helpdesk
function after_dispatch_to_default_hook (line 74) | def after_dispatch_to_default_hook(issue)
function add_attachments (line 78) | def add_attachments(obj)
function receive_issue_reply_with_helpdesk (line 93) | def receive_issue_reply_with_helpdesk(issue_id, from_journal=nil)
function custom_field_value (line 118) | def custom_field_value(issue,name)
function email_details (line 125) | def email_details
FILE: lib/redmine_helpdesk/mailer_patch.rb
type RedmineHelpdesk (line 1) | module RedmineHelpdesk
type MailerPatch (line 2) | module MailerPatch
function included (line 3) | def self.included(base) # :nodoc:
type InstanceMethods (line 12) | module InstanceMethods
function issue_edit_with_helpdesk (line 17) | def issue_edit_with_helpdesk(user, journal)
FILE: lib/tasks/local-db.rake
function get_env (line 5) | def get_env ()
function get_port (line 27) | def get_port ()
function get_image (line 42) | def get_image ()
FILE: test/fixtures/files/2006/07/060719210727_source.rb
class Greeter (line 2) | class Greeter
method initialize (line 3) | def initialize(name)
method salute (line 7) | def salute
FILE: test/functional/issues_controller_with_helpdesk_test.rb
class IssuesControllerWithHelpdeskTest (line 3) | class IssuesControllerWithHelpdeskTest < ActionController::TestCase
method setup (line 8) | def setup
method test_render_send_to_owner_checkbox (line 13) | def test_render_send_to_owner_checkbox
FILE: test/test_helper.rb
class TestHelper (line 16) | class TestHelper
method files_path (line 17) | def self.files_path
method fixture_path (line 20) | def self.fixture_path
class ActiveSupport::TestCase (line 29) | class ActiveSupport::TestCase
FILE: test/unit/helpdesk_mailer_test.rb
class HelpdeskMailerTest (line 3) | class HelpdeskMailerTest < ActionMailer::TestCase
method setup (line 10) | def setup
method teardown (line 19) | def teardown
method test_default_url_options (line 23) | def test_default_url_options
method test_email_headers (line 29) | def test_email_headers
method test_email_default_sender (line 47) | def test_email_default_sender
method test_email_helpdesk_sender (line 62) | def test_email_helpdesk_sender
method test_email_helpdesk_sender_with_phrase (line 71) | def test_email_helpdesk_sender_with_phrase
method test_first_reply (line 86) | def test_first_reply
method test_edit (line 99) | def test_edit
method test_fallback_message_id (line 117) | def test_fallback_message_id
method test_subject (line 138) | def test_subject
method test_attachments_added (line 148) | def test_attachments_added
FILE: test/unit/journal_patch_test.rb
class JournalPatchTest (line 3) | class JournalPatchTest < ActiveSupport::TestCase
method setup (line 10) | def setup
method teardown (line 14) | def teardown
method test_notification_not_sent_when_send_to_owner_false (line 18) | def test_notification_not_sent_when_send_to_owner_false
method test_notification_not_sent_when_notes_length_zero (line 30) | def test_notification_not_sent_when_notes_length_zero
method test_notification_note_sent_when_owner_email_blank (line 43) | def test_notification_note_sent_when_owner_email_blank
method test_notification_sent (line 61) | def test_notification_sent
FILE: test/unit/macro_expander_test.rb
class MacroExpanderTest (line 3) | class MacroExpanderTest < ActionMailer::TestCase
method setup (line 10) | def setup
method teardown (line 19) | def teardown
method test_expand_issue (line 23) | def test_expand_issue
method test_expand_project (line 43) | def test_expand_project
method test_expand_user (line 63) | def test_expand_user
method test_expand_user_cfs_w_not_existing (line 84) | def test_expand_user_cfs_w_not_existing
method test_expand_user_no_cfs (line 120) | def test_expand_user_no_cfs
method test_expand_base (line 142) | def test_expand_base
FILE: test/unit/mail_handler_patch_test.rb
class MailHandlerPatchTest (line 3) | class MailHandlerPatchTest < ActiveSupport::TestCase
method setup (line 10) | def setup
method teardown (line 15) | def teardown
method test_helpdesk_dispatch_not_supportclient (line 19) | def test_helpdesk_dispatch_not_supportclient
method test_helpdesk_dispatch_disabled_copy_to (line 35) | def test_helpdesk_dispatch_disabled_copy_to
method test_helpdesk_dispatch_anonymous_as_supportclient (line 56) | def test_helpdesk_dispatch_anonymous_as_supportclient
method test_helpdesk_dispatch_anonymous_as_supportclient_with_cc (line 75) | def test_helpdesk_dispatch_anonymous_as_supportclient_with_cc
method test_helpdesk_dispatch_supportclient (line 102) | def test_helpdesk_dispatch_supportclient
method submit_email (line 121) | def submit_email(filename, options={})
method assert_issue_created (line 127) | def assert_issue_created(issue)
Condensed preview — 94 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (144K chars).
[
{
"path": ".gitignore",
"chars": 24,
"preview": ".DS_Store\n*~\ntest/app/*\n"
},
{
"path": ".travis.yml",
"chars": 1428,
"preview": "language: ruby\nrvm:\n- 2.5\n- 2.6\n- jruby\nservices:\n - mysql\n - postgresql\nenv:\n global:\n - REDMINE_LANG=en\n - MYSQL_"
},
{
"path": "CHANGELOG.md",
"chars": 2450,
"preview": "0.0.20\n---\n* Make plugin compatibility with Redmine 5.0.x\n\n0.0.19\n---\n* Italian translation\n* Fix logger and use new set"
},
{
"path": "CONTRIBUTING.md",
"chars": 485,
"preview": "# Issues\n\nPlease note that I cannot provide user support for this open source project. All issues related to user suppor"
},
{
"path": "Gemfile",
"chars": 164,
"preview": "gem \"codeclimate-test-reporter\", group: :test, require: nil\nunless (Gem::Specification::find_all_by_name('rake').any?)\n "
},
{
"path": "LICENSE",
"chars": 1070,
"preview": "Copyright (c) 2012-2021 qutic development GmbH\n\nPermission is hereby granted, free of charge, to any person\nobtaining a "
},
{
"path": "README.md",
"chars": 10323,
"preview": "# Redmine Helpdesk\n\nLightweight helpdesk plugin for redmine. Adds the email sender-address of an anonymous supportclient"
},
{
"path": "Rakefile",
"chars": 48,
"preview": "Dir.glob('lib/tasks/*.rake').each { |r| load r}\n"
},
{
"path": "app/views/_issue_edit.erb",
"chars": 130,
"preview": "<%= check_box_tag('send_to_owner', \"true\", (send_to_owner_default == \"1\")) %> <%=h t('label_support_checkbox') %> (<%=h "
},
{
"path": "app/views/_issue_history.erb",
"chars": 93,
"preview": "<i style=\"display:block;margin-bottom:10px;\"><%=h t('email_was_send_to_supportclient') %></i>"
},
{
"path": "config/locales/de.yml",
"chars": 247,
"preview": "de:\n label_support_checkbox: \"E-Mail an Support-Anfragenden senden\"\n email_was_send_to_supportclient: \"Diese Antwort w"
},
{
"path": "config/locales/en.yml",
"chars": 139,
"preview": "en:\n label_support_checkbox: \"Send mail to supportclient\"\n email_was_send_to_supportclient: \"This answer was sent to t"
},
{
"path": "config/locales/es.yml",
"chars": 159,
"preview": "es:\n label_support_checkbox: \"Enviar correo al usuario de soporte\"\n email_was_send_to_supportclient: \"Esta respuesta h"
},
{
"path": "config/locales/it.yml",
"chars": 136,
"preview": "it:\n label_support_checkbox: \"Invia email al cliente\"\n email_was_send_to_supportclient: \"Questa risposta è stata invia"
},
{
"path": "config/locales/ja.yml",
"chars": 104,
"preview": "ja:\n label_support_checkbox: \"サポート顧客へメールを送信\"\n email_was_send_to_supportclient: \"この回答はサポート顧客へ送信されました。\"\n"
},
{
"path": "config/locales/pl.yml",
"chars": 135,
"preview": "pl:\n label_support_checkbox: \"Wyślij wiadomość do klienta\"\n email_was_send_to_supportclient: \"Wiadomość została wysłan"
},
{
"path": "config/locales/ru.yml",
"chars": 242,
"preview": "ru:\n label_support_checkbox: \"Отправить e-mail клиенту поддержки\"\n email_was_send_to_supportclient: \"Этот ответ был от"
},
{
"path": "config/locales/tr.yml",
"chars": 180,
"preview": "tr:\n label_support_checkbox: \"Bu yanıtı destek isteyen müşteriye e-posta ile gönder\"\n email_was_send_to_supportclient:"
},
{
"path": "config/locales/zh.yml",
"chars": 148,
"preview": "zh:\n label_support_checkbox: \"发送反馈Email至客户\"\n email_was_send_to_supportclient: \"反馈邮件已发送至客户。\"\n permission_treat_user_as"
},
{
"path": "db/migrate/001_create_custom_owner_email_field.rb",
"chars": 873,
"preview": "class CreateCustomOwnerEmailField < ActiveRecord::Migration[5.2]\n def self.up\n # fix PG:DuplicateColumn errors\n #"
},
{
"path": "db/migrate/002_create_custom_fields_for_reply.rb",
"chars": 794,
"preview": "class CreateCustomFieldsForReply < ActiveRecord::Migration[5.2]\n def self.up\n c = CustomField.new(\n :name => 'h"
},
{
"path": "db/migrate/003_create_custom_field_for_sender_email.rb",
"chars": 405,
"preview": "class CreateCustomFieldForSenderEmail < ActiveRecord::Migration[5.2]\n def self.up\n c = CustomField.new(\n :name "
},
{
"path": "db/migrate/004_create_custom_field_for_send_to_owner_default.rb",
"chars": 484,
"preview": "class CreateCustomFieldForSendToOwnerDefault < ActiveRecord::Migration[5.2]\n def self.up\n c = CustomField.new(\n "
},
{
"path": "db/migrate/005_add_treat_as_supportclient_to_anonymous.rb",
"chars": 484,
"preview": "class AddTreatAsSupportclientToAnonymous < ActiveRecord::Migration[5.2]\n def self.up\n anon_id = User.where(type: 'An"
},
{
"path": "db/migrate/006_append_footer_to_first_reply.rb",
"chars": 870,
"preview": "class AppendFooterToFirstReply < ActiveRecord::Migration[5.2]\n\n # Appends helpdesk-email-footer to helpdesk-first-reply"
},
{
"path": "db/migrate/007_create_custom_copy_to_field.rb",
"chars": 574,
"preview": "class CreateCustomCopyToField < ActiveRecord::Migration[5.2]\n def self.up\n c = CustomField.new(\n :name => 'copy"
},
{
"path": "db/migrate/008_create_custom_field_cc_handling.rb",
"chars": 380,
"preview": "class CreateCustomFieldCcHandling < ActiveRecord::Migration[5.2]\n def self.up\n c = CustomField.new(\n :name => '"
},
{
"path": "db/migrate/009_create_custom_field_for_reopen_closed_issues_by_email.rb",
"chars": 413,
"preview": "class CreateCustomFieldForReopenClosedIssuesByEmail < ActiveRecord::Migration[5.2]\n def self.up\n c = CustomField.new"
},
{
"path": "db/migrate/010_create_custom_field_for_reply_separator.rb",
"chars": 605,
"preview": "class CreateCustomFieldForReplySeparator < ActiveRecord::Migration[5.2]\n def self.up\n begin\n c = CustomField.ne"
},
{
"path": "init.rb",
"chars": 553,
"preview": "require 'redmine'\n$LOAD_PATH.unshift \"#{File.dirname(__FILE__)}/lib\"\nrequire 'helpdesk_hooks'\nrequire 'helpdesk_mailer'\n"
},
{
"path": "lib/helpdesk_hooks.rb",
"chars": 1996,
"preview": "class HelpdeskHooks < Redmine::Hook::Listener\n \n # render partial for 'Send mail to supportclient'\n def view_issues_e"
},
{
"path": "lib/helpdesk_mailer.rb",
"chars": 5125,
"preview": "#\n# With Rails 3 mail is send with the mail method. Sadly redmine\n# uses this method-name too in their mailer. This is t"
},
{
"path": "lib/macro_expander.rb",
"chars": 1597,
"preview": "module MacroExpander\n def expand_macros(string, issue, journal)\n e = Expander.new(string, issue, journal)\n e.expa"
},
{
"path": "lib/redmine_helpdesk/journal_patch.rb",
"chars": 1993,
"preview": "module RedmineHelpdesk\n module JournalPatch\n def self.included(base) # :nodoc:\n base.send(:include, InstanceMet"
},
{
"path": "lib/redmine_helpdesk/mail_handler_patch.rb",
"chars": 5363,
"preview": "module RedmineHelpdesk\n module MailHandlerPatch\n def self.included(base) # :nodoc:\n base.send(:include, Instanc"
},
{
"path": "lib/redmine_helpdesk/mailer_patch.rb",
"chars": 3496,
"preview": "module RedmineHelpdesk\n module MailerPatch\n def self.included(base) # :nodoc:\n base.send(:include, InstanceMeth"
},
{
"path": "lib/tasks/local-db.rake",
"chars": 1712,
"preview": "namespace :helpdesk do\n namespace :localdb do\n db_label = 'helpdesk-db'\n\n def get_env ()\n db_name = 'redmine"
},
{
"path": "lib/tasks/plugin_ci.rake",
"chars": 1738,
"preview": "namespace :helpdesk do\n plugin_root = File.expand_path('../../../', __FILE__)\n coverage_dir = \"#{plugin_root}/coverage"
},
{
"path": "lib/tasks/redmine.rake",
"chars": 2890,
"preview": "require 'fileutils'\n\nnamespace :helpdesk do\n namespace :redmine do\n plugin_root = File.expand_path('../../../', __FI"
},
{
"path": "test/confs/database_mysql.yml",
"chars": 1030,
"preview": "## MySQL configuration example\n## Data come from environment variables so the test suite can be run\n## on Travis or Jenk"
},
{
"path": "test/confs/database_postgres.yml",
"chars": 672,
"preview": "## PostgreSQL configuration example\n## Data come from environment variables so the test suite can be run\n## on Travis or"
},
{
"path": "test/confs/database_postgres_ext.yml",
"chars": 825,
"preview": "## PostgreSQL configuration example\n## Data come from environment variables so the test suite can be run\n## on Travis or"
},
{
"path": "test/confs/database_sqlite.yml",
"chars": 223,
"preview": "## SQLite3 configuration example\nproduction:\n adapter: sqlite3\n database: db/redmine.sqlite3\ndevelopment:\n adapter: s"
},
{
"path": "test/fixtures/2.6/attachments.yml",
"chars": 368,
"preview": "---\nattachments_001:\n created_on: 2006-07-19 21:07:27 +02:00\n container_type: Issue\n container_id: 1\n downloads: 0\n "
},
{
"path": "test/fixtures/2.6/custom_fields.yml",
"chars": 2515,
"preview": "---\ncustom_fields_001:\n name: owner-email\n regexp: \"\"\n type: IssueCustomField\n possible_values: \"\"\n id: 1\n is_requ"
},
{
"path": "test/fixtures/2.6/custom_fields_projects.yml",
"chars": 397,
"preview": "---\ncustom_fields_projects_001:\n custom_field_id: 1\n project_id: 1\ncustom_fields_projects_002:\n custom_field_id: 1\n "
},
{
"path": "test/fixtures/2.6/custom_fields_trackers.yml",
"chars": 528,
"preview": "---\ncustom_fields_trackers_001:\n custom_field_id: 1\n tracker_id: 1\ncustom_fields_trackers_002:\n custom_field_id: 1\n "
},
{
"path": "test/fixtures/2.6/custom_values.yml",
"chars": 1823,
"preview": "---\ncustom_values_001:\n customized_type: Project\n custom_field_id: 2\n customized_id: 1\n id: 1\n value: \"first reply\""
},
{
"path": "test/fixtures/2.6/enabled_modules.yml",
"chars": 586,
"preview": "---\nenabled_modules_001:\n name: issue_tracking\n project_id: 1\n id: 1\nenabled_modules_002:\n name: time_tracking\n pro"
},
{
"path": "test/fixtures/2.6/enumerations.yml",
"chars": 601,
"preview": "---\nenumerations_001:\n name: Low\n id: 1\n type: IssuePriority\n active: true\n position: 1\n position_name: lowest\nenu"
},
{
"path": "test/fixtures/2.6/issue_statuses.yml",
"chars": 589,
"preview": "--- \nissue_statuses_001: \n id: 1\n name: New\n is_default: true\n is_closed: false\n position: 1\nissue_statuses_002: \n "
},
{
"path": "test/fixtures/2.6/issues.yml",
"chars": 810,
"preview": "---\nissues_001:\n created_on: 2015-03-05 21:41:21 +02:00\n project_id: 1\n updated_on: 2015-03-05 22:05:50 +02:00\n prio"
},
{
"path": "test/fixtures/2.6/journal_details.yml",
"chars": 131,
"preview": "---\njournal_details_001:\n old_value:\n property: attachment\n id: 1\n value: 060719210727_source.rb\n prop_key: 1\n jou"
},
{
"path": "test/fixtures/2.6/journals.yml",
"chars": 652,
"preview": "---\njournals_001:\n created_on: <%= 2.days.ago.to_date.to_s(:db) %>\n notes: \"Journal notes\"\n id: 1\n journalized_type:"
},
{
"path": "test/fixtures/2.6/member_roles.yml",
"chars": 220,
"preview": "---\nmember_roles_001:\n id: 1\n role_id: 1\n member_id: 1\nmember_roles_002:\n id: 2\n role_id: 1\n member_id: 2\nmember_r"
},
{
"path": "test/fixtures/2.6/members.yml",
"chars": 590,
"preview": "---\nmembers_001:\n created_on: 2006-07-19 19:35:33 +02:00\n project_id: 1\n id: 1\n user_id: 1\n mail_notification: true"
},
{
"path": "test/fixtures/2.6/projects.yml",
"chars": 1097,
"preview": "---\nprojects_001:\n created_on: 2015-03-05 19:13:59 +02:00\n name: Helpdesk Project 1\n updated_on: 2015-03-05 22:53:01 "
},
{
"path": "test/fixtures/2.6/projects_trackers.yml",
"chars": 664,
"preview": "---\nprojects_trackers_001:\n project_id: 1\n tracker_id: 1\nprojects_trackers_002:\n project_id: 1\n tracker_id: 2\nprojec"
},
{
"path": "test/fixtures/2.6/roles.yml",
"chars": 4296,
"preview": "---\nroles_001:\n name: Manager\n id: 1\n builtin: 0\n issues_visibility: all\n permissions: |\n ---\n - :add_project"
},
{
"path": "test/fixtures/2.6/trackers.yml",
"chars": 237,
"preview": "--- \ntrackers_001: \n name: Bug\n id: 1\n is_in_chlog: true\n position: 1\ntrackers_002: \n name: Feature request\n id: 2"
},
{
"path": "test/fixtures/2.6/users.yml",
"chars": 1153,
"preview": "---\nusers_001:\n created_on: 2015-03-05 19:34:07 +02:00\n status: 1\n last_login_on:\n language: en\n # password = foo\n "
},
{
"path": "test/fixtures/3.0/attachments.yml",
"chars": 368,
"preview": "---\nattachments_001:\n created_on: 2006-07-19 21:07:27 +02:00\n container_type: Issue\n container_id: 1\n downloads: 0\n "
},
{
"path": "test/fixtures/3.0/custom_fields.yml",
"chars": 2515,
"preview": "---\ncustom_fields_001:\n name: owner-email\n regexp: \"\"\n type: IssueCustomField\n possible_values: \"\"\n id: 1\n is_requ"
},
{
"path": "test/fixtures/3.0/custom_fields_projects.yml",
"chars": 397,
"preview": "---\ncustom_fields_projects_001:\n custom_field_id: 1\n project_id: 1\ncustom_fields_projects_002:\n custom_field_id: 1\n "
},
{
"path": "test/fixtures/3.0/custom_fields_trackers.yml",
"chars": 528,
"preview": "---\ncustom_fields_trackers_001:\n custom_field_id: 1\n tracker_id: 1\ncustom_fields_trackers_002:\n custom_field_id: 1\n "
},
{
"path": "test/fixtures/3.0/custom_values.yml",
"chars": 1823,
"preview": "---\ncustom_values_001:\n customized_type: Project\n custom_field_id: 2\n customized_id: 1\n id: 1\n value: \"first reply\""
},
{
"path": "test/fixtures/3.0/email_addresses.yml",
"chars": 346,
"preview": "---\nemail_address_001:\n id: 1\n user_id: 1\n address: rhill@somenet.foo\n is_default: true\n created_on: 2006-07-19 19:"
},
{
"path": "test/fixtures/3.0/enabled_modules.yml",
"chars": 586,
"preview": "---\nenabled_modules_001:\n name: issue_tracking\n project_id: 1\n id: 1\nenabled_modules_002:\n name: time_tracking\n pro"
},
{
"path": "test/fixtures/3.0/enumerations.yml",
"chars": 601,
"preview": "---\nenumerations_001:\n name: Low\n id: 1\n type: IssuePriority\n active: true\n position: 1\n position_name: lowest\nenu"
},
{
"path": "test/fixtures/3.0/issue_statuses.yml",
"chars": 463,
"preview": "---\nissue_statuses_001:\n id: 1\n name: New\n is_closed: false\n position: 1\nissue_statuses_002:\n id: 2\n name: Assigne"
},
{
"path": "test/fixtures/3.0/issues.yml",
"chars": 810,
"preview": "---\nissues_001:\n created_on: 2015-03-05 21:41:21 +02:00\n project_id: 1\n updated_on: 2015-03-05 22:05:50 +02:00\n prio"
},
{
"path": "test/fixtures/3.0/journal_details.yml",
"chars": 131,
"preview": "---\njournal_details_001:\n old_value:\n property: attachment\n id: 1\n value: 060719210727_source.rb\n prop_key: 1\n jou"
},
{
"path": "test/fixtures/3.0/journals.yml",
"chars": 652,
"preview": "---\njournals_001:\n created_on: <%= 2.days.ago.to_date.to_s(:db) %>\n notes: \"Journal notes\"\n id: 1\n journalized_type:"
},
{
"path": "test/fixtures/3.0/member_roles.yml",
"chars": 220,
"preview": "---\nmember_roles_001:\n id: 1\n role_id: 1\n member_id: 1\nmember_roles_002:\n id: 2\n role_id: 1\n member_id: 2\nmember_r"
},
{
"path": "test/fixtures/3.0/members.yml",
"chars": 590,
"preview": "---\nmembers_001:\n created_on: 2006-07-19 19:35:33 +02:00\n project_id: 1\n id: 1\n user_id: 1\n mail_notification: true"
},
{
"path": "test/fixtures/3.0/projects.yml",
"chars": 1097,
"preview": "---\nprojects_001:\n created_on: 2015-03-05 19:13:59 +02:00\n name: Helpdesk Project 1\n updated_on: 2015-03-05 22:53:01 "
},
{
"path": "test/fixtures/3.0/projects_trackers.yml",
"chars": 664,
"preview": "---\nprojects_trackers_001:\n project_id: 1\n tracker_id: 1\nprojects_trackers_002:\n project_id: 1\n tracker_id: 2\nprojec"
},
{
"path": "test/fixtures/3.0/roles.yml",
"chars": 4296,
"preview": "---\nroles_001:\n name: Manager\n id: 1\n builtin: 0\n issues_visibility: all\n permissions: |\n ---\n - :add_project"
},
{
"path": "test/fixtures/3.0/trackers.yml",
"chars": 302,
"preview": "---\ntrackers_001:\n name: Bug\n id: 1\n is_in_chlog: true\n position: 1\n default_status_id: 1\ntrackers_002:\n name: Fea"
},
{
"path": "test/fixtures/3.0/users.yml",
"chars": 1099,
"preview": "---\nusers_001:\n created_on: 2015-03-05 19:34:07 +02:00\n status: 1\n last_login_on:\n language: en\n # password = foo\n "
},
{
"path": "test/fixtures/files/2006/07/060719210727_source.rb",
"chars": 143,
"preview": "# The Greeter class\nclass Greeter\n def initialize(name)\n @name = name.capitalize\n end\n\n def salute\n puts \"Hello"
},
{
"path": "test/fixtures/mail_handler/ticket_by_unknown_user.eml",
"chars": 508,
"preview": "Return-Path: <john.doe@somenet.foo>\nReceived: from osiris ([127.0.0.1])\n\tby OSIRIS\n\twith hMailServer ; Sun, 22 Jun 2008 "
},
{
"path": "test/fixtures/mail_handler/ticket_by_unknown_user_with_cc.eml",
"chars": 617,
"preview": "Return-Path: <john.doe@somenet.foo>\nReceived: from osiris ([127.0.0.1])\n\tby OSIRIS\n\twith hMailServer ; Sun, 22 Jun 2008 "
},
{
"path": "test/fixtures/mail_handler/ticket_by_user_1.eml",
"chars": 502,
"preview": "Return-Path: <rhill@somenet.foo>\nReceived: from osiris ([127.0.0.1])\n\tby OSIRIS\n\twith hMailServer ; Sun, 22 Jun 2008 12:"
},
{
"path": "test/fixtures/mail_handler/ticket_by_user_2.eml",
"chars": 506,
"preview": "Return-Path: <dlopper@somenet.foo>\nReceived: from osiris ([127.0.0.1])\n\tby OSIRIS\n\twith hMailServer ; Sun, 22 Jun 2008 1"
},
{
"path": "test/fixtures/mail_handler/ticket_reply.eml",
"chars": 1938,
"preview": "Return-Path: <jsmith@somenet.foo>\nReceived: from osiris ([127.0.0.1])\n\tby OSIRIS\n\twith hMailServer ; Sat, 21 Jun 2008 18"
},
{
"path": "test/fixtures/mail_handler/ticket_with_attachment.eml",
"chars": 16308,
"preview": "Return-Path: <jsmith@somenet.foo>\nReceived: from osiris ([127.0.0.1])\n\tby OSIRIS\n\twith hMailServer ; Sat, 21 Jun 2008 15"
},
{
"path": "test/fixtures/mail_handler/ticket_with_attributes.eml",
"chars": 1901,
"preview": "Return-Path: <jsmith@somenet.foo>\nReceived: from osiris ([127.0.0.1])\n\tby OSIRIS\n\twith hMailServer ; Sun, 22 Jun 2008 12"
},
{
"path": "test/functional/issues_controller_with_helpdesk_test.rb",
"chars": 4050,
"preview": "require File.dirname(__FILE__) + '/../test_helper'\n\nclass IssuesControllerWithHelpdeskTest < ActionController::TestCase\n"
},
{
"path": "test/test_helper.rb",
"chars": 794,
"preview": "require \"simplecov\"\nrequire \"codeclimate-test-reporter\"\n\nSimpleCov.use_merging true\nSimpleCov.merge_timeout 3600\nSimpleC"
},
{
"path": "test/unit/helpdesk_mailer_test.rb",
"chars": 5866,
"preview": "require File.dirname(__FILE__) + '/../test_helper'\n\nclass HelpdeskMailerTest < ActionMailer::TestCase\n include Redmine:"
},
{
"path": "test/unit/journal_patch_test.rb",
"chars": 2054,
"preview": "require File.dirname(__FILE__) + '/../test_helper'\n\nclass JournalPatchTest < ActiveSupport::TestCase\n include Redmine::"
},
{
"path": "test/unit/macro_expander_test.rb",
"chars": 5278,
"preview": "require File.dirname(__FILE__) + '/../test_helper'\n\nclass MacroExpanderTest < ActionMailer::TestCase\n include Redmine::"
},
{
"path": "test/unit/mail_handler_patch_test.rb",
"chars": 5081,
"preview": "require File.dirname(__FILE__) + '/../test_helper'\n\nclass MailHandlerPatchTest < ActiveSupport::TestCase\n include Redmi"
}
]
About this extraction
This page contains the full source code of the jfqd/redmine_helpdesk GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 94 files (130.0 KB), approximately 46.8k tokens, and a symbol index with 124 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.